From 18b507e1b20fc6c32ee50f00fb910a59110c1a1d Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Wed, 20 Oct 2010 19:52:29 +0200 Subject: [PATCH 1/1] Check in Bonanza Feliz 0.0 --- Makefile | 45 ++ Makefile.vs | 55 ++ attack.c | 319 +++++++++ bitop.c | 304 ++++++++ bonanza.ico | Bin 0 -> 4286 bytes bonanza.rc | 1 + book.c | 1230 ++++++++++++++++++++++++++++++++ book_anti.csa | 6 + csa.c | 965 ++++++++++++++++++++++++++ data.c | 327 +++++++++ debug.c | 242 +++++++ dek.c | 314 +++++++++ evaldiff.c | 216 ++++++ evaluate.c | 494 +++++++++++++ gencap.c | 460 ++++++++++++ genchk.c | 1441 ++++++++++++++++++++++++++++++++++++++ gendrop.c | 194 ++++++ genevasn.c | 651 +++++++++++++++++ gennocap.c | 370 ++++++++++ hash.c | 1107 +++++++++++++++++++++++++++++ ini.c | 1097 +++++++++++++++++++++++++++++ io.c | 772 +++++++++++++++++++++ iterate.c | 871 +++++++++++++++++++++++ lan.txt | 30 + learn1.c | 1074 ++++++++++++++++++++++++++++ learn2.c | 856 +++++++++++++++++++++++ main.c | 154 ++++ makemove.c | 508 ++++++++++++++ mate1ply.c | 2155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mate3.c | 792 +++++++++++++++++++++ movgenex.c | 415 +++++++++++ next.c | 355 ++++++++++ param.h | 15 + phash.c | 275 ++++++++ ponder.c | 106 +++ problem.c | 102 +++ proce.c | 1654 +++++++++++++++++++++++++++++++++++++++++++ quiesrch.c | 222 ++++++ rand.c | 90 +++ readme.txt | 562 +++++++++++++++ root.c | 249 +++++++ sckt.c | 364 ++++++++++ search.c | 1411 +++++++++++++++++++++++++++++++++++++ searchr.c | 740 ++++++++++++++++++++ shogi.h | 1444 ++++++++++++++++++++++++++++++++++++++ swap.c | 391 +++++++++++ thread.c | 458 ++++++++++++ time.c | 300 ++++++++ unmake.c | 326 +++++++++ utility.c | 689 ++++++++++++++++++ valid.c | 197 ++++++ 51 files changed, 27415 insertions(+), 0 deletions(-) create mode 100644 Makefile create mode 100644 Makefile.vs create mode 100644 attack.c create mode 100644 bitop.c create mode 100644 bonanza.ico create mode 100644 bonanza.rc create mode 100644 book.c create mode 100644 book_anti.csa create mode 100644 csa.c create mode 100644 data.c create mode 100644 debug.c create mode 100644 dek.c create mode 100644 evaldiff.c create mode 100644 evaluate.c create mode 100644 gencap.c create mode 100644 genchk.c create mode 100644 gendrop.c create mode 100644 genevasn.c create mode 100644 gennocap.c create mode 100644 hash.c create mode 100644 ini.c create mode 100644 io.c create mode 100644 iterate.c create mode 100644 lan.txt create mode 100644 learn1.c create mode 100644 learn2.c create mode 100644 main.c create mode 100644 makemove.c create mode 100644 mate1ply.c create mode 100644 mate3.c create mode 100644 movgenex.c create mode 100644 next.c create mode 100644 param.h create mode 100644 phash.c create mode 100644 ponder.c create mode 100644 problem.c create mode 100644 proce.c create mode 100644 quiesrch.c create mode 100644 rand.c create mode 100644 readme.txt create mode 100644 root.c create mode 100644 sckt.c create mode 100644 search.c create mode 100644 searchr.c create mode 100644 shogi.h create mode 100644 swap.c create mode 100644 thread.c create mode 100644 time.c create mode 100644 unmake.c create mode 100644 utility.c create mode 100644 valid.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..856e931 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +OBJS =data.o main.o io.o proce.o utility.o ini.o attack.o book.o makemove.o \ + unmake.o time.o csa.o valid.o bitop.o iterate.o searchr.o search.o \ + quiesrch.o evaluate.o swap.o hash.o root.o next.o movgenex.o \ + genevasn.o gencap.o gennocap.o gendrop.o mate1ply.o rand.o learn1.o \ + learn2.o evaldiff.o problem.o ponder.o thread.o sckt.o debug.o mate3.o \ + genchk.o phash.o + +# Compile Options +# +# -DNDEBUG (DEBUG) builds release (debug) version of Bonanza. +# -DMINIMUM disables some auxiliary functions that are not necessary to +# play a game, e.g., book composition and optimization of +# evaluation function. +# -DTLP enables thread-level parallel search. +# -DMPV enables multi-PV search. +# -DCSA_LAN enables bonanza to talk CSA Shogi TCP/IP protcol. +# -DMNJ_LAN enables a client-mode of cluster computing. +# -DNO_LOGGING suppresses dumping log files. + +OPT =-DNDEBUG -DMINIMUM -DTLP -DCSA_LAN -DMNJ_LAN + +help: + @echo "try targets as:" + @echo + @echo " gcc" + @echo " icc" + +gcc: + $(MAKE) CC=gcc CFLAGS='-std=gnu99 -O3 -Wall $(OPT)' LDFLAG1='-lm -lpthread' bonanza + +icc: + $(MAKE) CC=icc CFLAGS='-w2 $(OPT) -std=gnu99 -O2 -ipo' LDFLAG1='-static -ipo -pthread' bonanza + +bonanza : $(OBJS) + $(CC) $(LDFLAG1) -o bonanza $(OBJS) $(LDFLAG2) + +$(OBJS) : shogi.h param.h + +.c.o : + $(CC) -c $(CFLAGS) $*.c + +clean : + rm *.o + rm -fr profdir + rm bonanza diff --git a/Makefile.vs b/Makefile.vs new file mode 100644 index 0000000..6a966ba --- /dev/null +++ b/Makefile.vs @@ -0,0 +1,55 @@ +help: + @echo try targets as: + @echo cl + @echo icl + +# Compile Options +# +# /DNDEBUG (DEBUG) builds release (debug) version of Bonanza. +# /DMINIMUM disables some auxiliary functions that are not necessary to +# play a game, e.g., book composition and optimization of +# evaluation function. +# /DTLP enables thread-level parallel search. +# /DMPV enables multi-PV search. +# /DCSA_LAN enables bonanza to talk CSA Shogi TCP/IP protcol. +# /DMNJ_LAN enables a client-mode of distributed computing. +# /DDEKUNOBOU enables dekunobou interface (avairable only for Windows). +# /DCSASHOGI builds an engine for CSA Shogi (avairable only for +# Windows). +# /DNO_LOGGING suppresses dumping log files. + +FLAG = /DNDEBUG /DMINIMUM /DTLP /DMPV /DCSASHOGI /DNO_LOGGING + +OBJS = data.obj main.obj io.obj proce.obj ini.obj utility.obj attack.obj\ + gencap.obj gennocap.obj gendrop.obj genevasn.obj mate3.obj genchk.obj\ + movgenex.obj makemove.obj unmake.obj time.obj csa.obj valid.obj\ + next.obj search.obj searchr.obj book.obj iterate.obj quiesrch.obj\ + swap.obj evaluate.obj root.obj hash.obj mate1ply.obj bitop.obj\ + rand.obj learn1.obj learn2.obj evaldiff.obj problem.obj ponder.obj\ + thread.obj dek.obj sckt.obj debug.obj phash.obj + +cl: + $(MAKE) -f Makefile.vs bonanza.exe CC="cl" LD="link"\ + CFLAGS="$(FLAG) /MT /W4 /nologo /O2 /Ob2 /Gr /GS- /GL"\ + LDFLAGS="/NOLOGO /out:bonanza.exe /LTCG" + +icl: + $(MAKE) -f Makefile.vs bonanza.exe CC="icl" LD="icl"\ + CFLAGS="/nologo $(FLAG) /Wall /O2 /Qipo /Gr"\ + LDFLAGS="/nologo /Febonanza.exe" + +bonanza.exe : $(OBJS) bonanza.res + $(LD) $(LDFLAGS) $(OBJS) bonanza.res User32.lib Ws2_32.lib + +$(OBJS) : shogi.h param.h + +bonanza.res : bonanza.rc bonanza.ico + rc /fobonanza.res bonanza.rc + +.c.obj : + $(CC) $(CFLAGS) /c $*.c + +clean : + del /q *.obj + del /q *.res + del /q bonanza.exe diff --git a/attack.c b/attack.c new file mode 100644 index 0000000..2c9cf78 --- /dev/null +++ b/attack.c @@ -0,0 +1,319 @@ +#include +#include +#include "shogi.h" + + +unsigned int +is_pinned_on_white_king( const tree_t * restrict ptree, int isquare, + int idirec ) +{ + unsigned int ubb_attacks; + bitboard_t bb_attacks, bb_attacker; + + switch ( idirec ) + { + case direc_rank: + ubb_attacks = AttackRank( isquare ); + if ( ubb_attacks & (BB_WKING.p[aslide[isquare].ir0]) ) + { + return ubb_attacks & BB_B_RD.p[aslide[isquare].ir0]; + } + break; + + case direc_file: + bb_attacks = AttackFile( isquare ); + if ( BBContract( bb_attacks, BB_WKING ) ) + { + BBAnd( bb_attacker, BB_BLANCE, abb_plus_rays[isquare] ); + BBOr( bb_attacker, bb_attacker, BB_B_RD ); + return BBContract( bb_attacks, bb_attacker ); /* return! */ + } + break; + + case direc_diag1: + bb_attacks = AttackDiag1( isquare ); + if ( BBContract( bb_attacks, BB_WKING ) ) + { + return BBContract( bb_attacks, BB_B_BH ); /* return! */ + } + break; + + default: + assert( idirec == direc_diag2 ); + bb_attacks = AttackDiag2( isquare ); + if ( BBContract( bb_attacks, BB_WKING ) ) + { + return BBContract( bb_attacks, BB_B_BH ); /* return! */ + } + break; + } + + return 0; +} + + +unsigned int +is_pinned_on_black_king( const tree_t * restrict ptree, int isquare, + int idirec ) +{ + unsigned int ubb_attacks; + bitboard_t bb_attacks, bb_attacker; + + switch ( idirec ) + { + case direc_rank: + ubb_attacks = AttackRank( isquare ); + if ( ubb_attacks & (BB_BKING.p[aslide[isquare].ir0]) ) + { + return ubb_attacks & BB_W_RD.p[aslide[isquare].ir0]; + } + break; + + case direc_file: + bb_attacks = AttackFile( isquare ); + if ( BBContract( bb_attacks, BB_BKING ) ) + { + BBAnd( bb_attacker, BB_WLANCE, abb_minus_rays[isquare] ); + BBOr( bb_attacker, bb_attacker, BB_W_RD ); + return BBContract( bb_attacks, bb_attacker ); /* return! */ + } + break; + + case direc_diag1: + bb_attacks = AttackDiag1( isquare ); + if ( BBContract( bb_attacks, BB_BKING ) ) + { + return BBContract( bb_attacks, BB_W_BH ); /* return! */ + } + break; + + default: + assert( idirec == direc_diag2 ); + bb_attacks = AttackDiag2( isquare ); + if ( BBContract( bb_attacks, BB_BKING ) ) + { + return BBContract( bb_attacks, BB_W_BH ); /* return! */ + } + break; + } + return 0; +} + + +/* perpetual check detections are omitted. */ +int +is_mate_b_pawn_drop( tree_t * restrict ptree, int sq_drop ) +{ + bitboard_t bb, bb_sum, bb_move; + int iwk, ito, iret, ifrom, idirec; + + BBAnd( bb_sum, BB_WKNIGHT, abb_b_knight_attacks[sq_drop] ); + + BBAndOr( bb_sum, BB_WSILVER, abb_b_silver_attacks[sq_drop] ); + BBAndOr( bb_sum, BB_WTGOLD, abb_b_gold_attacks[sq_drop] ); + + AttackBishop( bb, sq_drop ); + BBAndOr( bb_sum, BB_W_BH, bb ); + + AttackRook( bb, sq_drop ); + BBAndOr( bb_sum, BB_W_RD, bb ); + + BBOr( bb, BB_WHORSE, BB_WDRAGON ); + BBAndOr( bb_sum, bb, abb_king_attacks[sq_drop] ); + + while ( BBToU( bb_sum ) ) + { + ifrom = FirstOne( bb_sum ); + Xor( ifrom, bb_sum ); + + if ( IsDiscoverWK( ifrom, sq_drop ) ) { continue; } + return 0; + } + + iwk = SQ_WKING; + iret = 1; + Xor( sq_drop, BB_BOCCUPY ); + XorFile( sq_drop, OCCUPIED_FILE ); + XorDiag2( sq_drop, OCCUPIED_DIAG2 ); + XorDiag1( sq_drop, OCCUPIED_DIAG1 ); + + BBNot( bb_move, BB_WOCCUPY ); + BBAnd( bb_move, bb_move, abb_king_attacks[iwk] ); + while ( BBToU( bb_move ) ) + { + ito = FirstOne( bb_move ); + if ( ! is_white_attacked( ptree, ito ) ) + { + iret = 0; + break; + } + Xor( ito, bb_move ); + } + + Xor( sq_drop, BB_BOCCUPY ); + XorFile( sq_drop, OCCUPIED_FILE ); + XorDiag2( sq_drop, OCCUPIED_DIAG2 ); + XorDiag1( sq_drop, OCCUPIED_DIAG1 ); + + return iret; +} + + +int +is_mate_w_pawn_drop( tree_t * restrict ptree, int sq_drop ) +{ + bitboard_t bb, bb_sum, bb_move; + int ibk, ito, ifrom, iret, idirec; + + BBAnd( bb_sum, BB_BKNIGHT, abb_w_knight_attacks[sq_drop] ); + + BBAndOr( bb_sum, BB_BSILVER, abb_w_silver_attacks[sq_drop] ); + BBAndOr( bb_sum, BB_BTGOLD, abb_w_gold_attacks[sq_drop] ); + + AttackBishop( bb, sq_drop ); + BBAndOr( bb_sum, BB_B_BH, bb ); + + AttackRook( bb, sq_drop ); + BBAndOr( bb_sum, BB_B_RD, bb ); + + BBOr( bb, BB_BHORSE, BB_BDRAGON ); + BBAndOr( bb_sum, bb, abb_king_attacks[sq_drop] ); + + while ( BBToU( bb_sum ) ) + { + ifrom = FirstOne( bb_sum ); + Xor( ifrom, bb_sum ); + + if ( IsDiscoverBK( ifrom, sq_drop ) ) { continue; } + return 0; + } + + ibk = SQ_BKING; + iret = 1; + Xor( sq_drop, BB_WOCCUPY ); + XorFile( sq_drop, OCCUPIED_FILE ); + XorDiag2( sq_drop, OCCUPIED_DIAG2 ); + XorDiag1( sq_drop, OCCUPIED_DIAG1 ); + + BBNot( bb_move, BB_BOCCUPY ); + BBAnd( bb_move, bb_move, abb_king_attacks[ibk] ); + while ( BBToU( bb_move ) ) + { + ito = FirstOne( bb_move ); + if ( ! is_black_attacked( ptree, ito ) ) + { + iret = 0; + break; + } + Xor( ito, bb_move ); + } + + Xor( sq_drop, BB_WOCCUPY ); + XorFile( sq_drop, OCCUPIED_FILE ); + XorDiag2( sq_drop, OCCUPIED_DIAG2 ); + XorDiag1( sq_drop, OCCUPIED_DIAG1 ); + + return iret; +} + + +bitboard_t +attacks_to_piece( const tree_t * restrict ptree, int sq ) +{ + bitboard_t bb_ret, bb_attacks, bb; + + BBIni( bb_ret ); + if ( sq < rank9*nfile && BOARD[sq+nfile] == pawn ) + { + bb_ret = abb_mask[sq+nfile]; + } + if ( sq >= nfile && BOARD[sq-nfile] == -pawn ) + { + BBOr( bb_ret, bb_ret, abb_mask[sq-nfile] ); + } + + BBAndOr( bb_ret, BB_BKNIGHT, abb_w_knight_attacks[sq] ); + BBAndOr( bb_ret, BB_WKNIGHT, abb_b_knight_attacks[sq] ); + + BBAndOr( bb_ret, BB_BSILVER, abb_w_silver_attacks[sq] ); + BBAndOr( bb_ret, BB_WSILVER, abb_b_silver_attacks[sq] ); + + BBAndOr( bb_ret, BB_BTGOLD, abb_w_gold_attacks[sq] ); + BBAndOr( bb_ret, BB_WTGOLD, abb_b_gold_attacks[sq] ); + + BBOr( bb, BB_B_HDK, BB_W_HDK ); + BBAndOr( bb_ret, bb, abb_king_attacks[sq] ); + + BBOr( bb, BB_B_BH, BB_W_BH ); + AttackBishop( bb_attacks, sq ); + BBAndOr( bb_ret, bb, bb_attacks ); + + BBOr( bb, BB_B_RD, BB_W_RD ); + bb_ret.p[aslide[sq].ir0] + |= bb.p[aslide[sq].ir0] & AttackRank( sq ); + + BBAndOr( bb, BB_BLANCE, abb_plus_rays[sq] ); + BBAndOr( bb, BB_WLANCE, abb_minus_rays[sq] ); + bb_attacks = AttackFile( sq ); + BBAndOr( bb_ret, bb, bb_attacks ); + + return bb_ret; +} + + +unsigned int +is_white_attacked( const tree_t * restrict ptree, int sq ) +{ + bitboard_t bb; + unsigned int u; + + u = BBContract( BB_BPAWN_ATK, abb_mask[sq] ); + u |= BBContract( BB_BKNIGHT, abb_w_knight_attacks[sq] ); + u |= BBContract( BB_BSILVER, abb_w_silver_attacks[sq] ); + u |= BBContract( BB_BTGOLD, abb_w_gold_attacks[sq] ); + u |= BBContract( BB_B_HDK, abb_king_attacks[sq] ); + + AttackBishop( bb, sq ); + u |= BBContract( BB_B_BH, bb ); + + u |= BB_B_RD.p[aslide[sq].ir0] & AttackRank( sq ); + + bb = AttackFile( sq ); + u |= ( ( BB_BLANCE.p[0] & abb_plus_rays[sq].p[0] ) + | BB_B_RD.p[0] ) & bb.p[0]; + u |= ( ( BB_BLANCE.p[1] & abb_plus_rays[sq].p[1] ) + | BB_B_RD.p[1] ) & bb.p[1]; + u |= ( ( BB_BLANCE.p[2] & abb_plus_rays[sq].p[2] ) + | BB_B_RD.p[2] ) & bb.p[2]; + + return u; +} + + +unsigned int +is_black_attacked( const tree_t * restrict ptree, int sq ) +{ + bitboard_t bb; + unsigned int u; + + u = BBContract( BB_WPAWN_ATK, abb_mask[sq] ); + u |= BBContract( BB_WKNIGHT, abb_b_knight_attacks[sq] ); + u |= BBContract( BB_WSILVER, abb_b_silver_attacks[sq] ); + u |= BBContract( BB_WTGOLD, abb_b_gold_attacks[sq] ); + u |= BBContract( BB_W_HDK, abb_king_attacks[sq] ); + + AttackBishop( bb, sq ); + u |= BBContract( BB_W_BH, bb ); + + u |= BB_W_RD.p[aslide[sq].ir0] & AttackRank( sq ); + + bb = AttackFile( sq ); + u |= ( ( BB_WLANCE.p[0] & abb_minus_rays[sq].p[0] ) + | BB_W_RD.p[0] ) & bb.p[0]; + u |= ( ( BB_WLANCE.p[1] & abb_minus_rays[sq].p[1] ) + | BB_W_RD.p[1] ) & bb.p[1]; + u |= ( ( BB_WLANCE.p[2] & abb_minus_rays[sq].p[2] ) + | BB_W_RD.p[2] ) & bb.p[2]; + + return u; +} diff --git a/bitop.c b/bitop.c new file mode 100644 index 0000000..13fbe58 --- /dev/null +++ b/bitop.c @@ -0,0 +1,304 @@ +#include "shogi.h" + +int +popu_count012( unsigned int u0, unsigned int u1, unsigned int u2 ) +{ + int counter = 0; + while ( u0 ) { counter++; u0 &= u0 - 1U; } + while ( u1 ) { counter++; u1 &= u1 - 1U; } + while ( u2 ) { counter++; u2 &= u2 - 1U; } + return counter; +} + + +#if defined(_MSC_VER) + +int +first_one012( unsigned int u0, unsigned int u1, unsigned int u2 ) +{ + unsigned long index; + + if ( _BitScanReverse( &index, u0 ) ) { return 26 - index; } + if ( _BitScanReverse( &index, u1 ) ) { return 53 - index; } + _BitScanReverse( &index, u2 ); + return 80 - index; +} + +int +last_one210( unsigned int u2, unsigned int u1, unsigned int u0 ) +{ + unsigned long index; + + if ( _BitScanForward( &index, u2 ) ) { return 80 - index; } + if ( _BitScanForward( &index, u1 ) ) { return 53 - index; } + _BitScanForward( &index, u0 ); + return 26 - index; +} + +int +first_one01( unsigned int u0, unsigned int u1 ) +{ + unsigned long index; + + if ( _BitScanReverse( &index, u0 ) ) { return 26 - index; } + _BitScanReverse( &index, u1 ); + return 53 - index; +} + +int +first_one12( unsigned int u1, unsigned int u2 ) +{ + unsigned long index; + + if ( _BitScanReverse( &index, u1 ) ) { return 53 - index; } + _BitScanReverse( &index, u2 ); + return 80 - index; +} + +int +last_one01( unsigned int u0, unsigned int u1 ) +{ + unsigned long index; + + if ( _BitScanForward( &index, u1 ) ) { return 53 - index; } + _BitScanForward( &index, u0 ); + return 26 - index; +} + +int +last_one12( unsigned int u1, unsigned u2 ) +{ + unsigned long index; + + if ( _BitScanForward( &index, u2 ) ) { return 80 - index; } + _BitScanForward( &index, u1 ); + return 53 - index; +} + +int +first_one1( unsigned int u1 ) +{ + unsigned long index; + + _BitScanReverse( &index, u1 ); + return 53 - index; +} + +int +first_one2( unsigned int u2 ) +{ + unsigned long index; + + _BitScanReverse( &index, u2 ); + return 80 - index; +} + +int +last_one0( unsigned int u0 ) +{ + unsigned long index; + + _BitScanForward( &index, u0 ); + return 26 - index; +} + +int +last_one1( unsigned int u1 ) +{ + unsigned long index; + + _BitScanForward( &index, u1 ); + return 53 - index; +} + +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +int +first_one012( unsigned int u0, unsigned int u1, unsigned int u2 ) +{ + if ( u0 ) { return __builtin_clz( u0 ) - 5; } + if ( u1 ) { return __builtin_clz( u1 ) + 22; } + return __builtin_clz( u2 ) + 49; +} + + +int +last_one210( unsigned int u2, unsigned int u1, unsigned int u0 ) +{ + if ( u2 ) { return 80 - __builtin_ctz( u2 ); } + if ( u1 ) { return 53 - __builtin_ctz( u1 ); } + return 26 - __builtin_ctz( u0 ); +} + + +int +first_one01( unsigned int u0, unsigned int u1 ) +{ + if ( u0 ) { return __builtin_clz( u0 ) - 5; } + return __builtin_clz( u1 ) + 22; +} + + +int +first_one12( unsigned int u1, unsigned int u2 ) +{ + if ( u1 ) { return __builtin_clz( u1 ) + 22; } + return __builtin_clz( u2 ) + 49; +} + + +int +last_one01( unsigned int u0, unsigned int u1 ) +{ + if ( u1 ) { return 53 - __builtin_ctz( u1 ); } + return 26 - __builtin_ctz( u0 ); +} + + +int +last_one12( unsigned int u1, unsigned int u2 ) +{ + if ( u2 ) { return 80 - __builtin_ctz( u2 ); } + return 53 - __builtin_ctz( u1 ); +} + + +int first_one1( unsigned int u1 ) { return __builtin_clz( u1 ) + 22; } +int first_one2( unsigned int u2 ) { return __builtin_clz( u2 ) + 49; } +int last_one0( unsigned int u0 ) { return 26 - __builtin_ctz( u0 ); } +int last_one1( unsigned int u1 ) { return 53 - __builtin_ctz( u1 ); } + +#else + +int +first_one012( unsigned int u0, unsigned int u1, unsigned int u2 ) +{ + if ( u0 & 0x7fc0000 ) { return aifirst_one[u0>>18] + 0; } + if ( u0 & 0x7fffe00 ) { return aifirst_one[u0>> 9] + 9; } + if ( u0 & 0x7ffffff ) { return aifirst_one[u0 ] + 18; } + + if ( u1 & 0x7fc0000 ) { return aifirst_one[u1>>18] + 27; } + if ( u1 & 0x7fffe00 ) { return aifirst_one[u1>> 9] + 36; } + if ( u1 & 0x7ffffff ) { return aifirst_one[u1 ] + 45; } + + if ( u2 & 0x7fc0000 ) { return aifirst_one[u2>>18] + 54; } + if ( u2 & 0x7fffe00 ) { return aifirst_one[u2>> 9] + 63; } + return aifirst_one[u2] + 72; +} + + +int +last_one210( unsigned int u2, unsigned int u1, unsigned int u0 ) +{ + unsigned int j; + + j = u2 & 0x00001ff; if ( j ) { return ailast_one[j ] + 72; } + j = u2 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 63; } + if ( u2 & 0x7ffffff ) { return ailast_one[u2>>18] + 54; } + + j = u1 & 0x00001ff; if ( j ) { return ailast_one[j ] + 45; } + j = u1 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 36; } + if ( u1 & 0x7ffffff ) { return ailast_one[u1>>18] + 27; } + + j = u0 & 0x00001ff; if ( j ) { return ailast_one[j ] + 18; } + j = u0 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 9; } + return ailast_one[u0>>18]; +} + + +int +first_one01( unsigned int u0, unsigned int u1 ) +{ + if ( u0 & 0x7fc0000 ) { return aifirst_one[u0>>18] + 0; } + if ( u0 & 0x7fffe00 ) { return aifirst_one[u0>> 9] + 9; } + if ( u0 & 0x7ffffff ) { return aifirst_one[u0 ] + 18; } + + if ( u1 & 0x7fc0000 ) { return aifirst_one[u1>>18] + 27; } + if ( u1 & 0x7fffe00 ) { return aifirst_one[u1>> 9] + 36; } + return aifirst_one[ u1 ] + 45; +} + + +int +first_one12( unsigned int u1, unsigned int u2 ) +{ + if ( u1 & 0x7fc0000 ) { return aifirst_one[u1>>18] + 27; } + if ( u1 & 0x7fffe00 ) { return aifirst_one[u1>> 9] + 36; } + if ( u1 & 0x7ffffff ) { return aifirst_one[u1 ] + 45; } + + if ( u2 & 0x7fc0000 ) { return aifirst_one[u2>>18] + 54; } + if ( u2 & 0x7fffe00 ) { return aifirst_one[u2>> 9] + 63; } + return aifirst_one[ u2 ] + 72; +} + + +int +last_one01( unsigned int u0, unsigned int u1 ) +{ + unsigned int j; + + j = u1 & 0x00001ff; if ( j ) { return ailast_one[j ] + 45; } + j = u1 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 36; } + if ( u1 & 0x7ffffff ) { return ailast_one[u1>>18] + 27; } + + j = u0 & 0x00001ff; if ( j ) { return ailast_one[j ] + 18; } + j = u0 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 9; } + return ailast_one[u0>>18]; +} + + +int +last_one12( unsigned int u1, unsigned int u2 ) +{ + unsigned int j; + + j = u2 & 0x00001ff; if ( j ) { return ailast_one[j ] + 72; } + j = u2 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 63; } + if ( u2 & 0x7ffffff ) { return ailast_one[u2>>18] + 54; } + + j = u1 & 0x00001ff; if ( j ) { return ailast_one[j ] + 45; } + j = u1 & 0x003ffff; if ( j ) { return ailast_one[j>> 9] + 36; } + return ailast_one[u1>>18] + 27; +} + + +int +first_one1( unsigned int u1 ) +{ + if ( u1 & 0x7fc0000U ) { return aifirst_one[u1>>18] + 27; } + if ( u1 & 0x7fffe00U ) { return aifirst_one[u1>> 9] + 36; } + return aifirst_one[u1] + 45; +} + + +int +first_one2( unsigned int u2 ) +{ + if ( u2 & 0x7fc0000U ) { return aifirst_one[u2>>18] + 54; } + if ( u2 & 0x7fffe00U ) { return aifirst_one[u2>> 9] + 63; } + return aifirst_one[u2] + 72; +} + + +int +last_one0( unsigned int i ) +{ + unsigned int j; + + j = i & 0x00001ffU; if ( j ) { return ailast_one[j ] + 18; } + j = i & 0x003ffffU; if ( j ) { return ailast_one[j>> 9] + 9; } + return ailast_one[i>>18]; +} + + +int +last_one1( unsigned int u1 ) +{ + unsigned int j; + + j = u1 & 0x00001ffU; if ( j ) { return ailast_one[j ] + 45; } + j = u1 & 0x003ffffU; if ( j ) { return ailast_one[j>> 9] + 36; } + return ailast_one[u1>>18] + 27; +} + +#endif diff --git a/bonanza.ico b/bonanza.ico new file mode 100644 index 0000000000000000000000000000000000000000..fc71e771caabd9229c14fdd1cbda99182fd366f8 GIT binary patch literal 4286 zcmeHJOLoH`45T;cs_QJf=K?)V?$jIfFus$kK?nvJ97ytByGZ&JFvik+gwqh-$6A3e|O=$q1Gj?b;$Zl1L~IR`Z3I-@yibUlzf;5d5D)=>xN z@|a`ZgTmionc+}<@EGFsnM@qGHt{i@qj96Xs)6;xJb(4kY(o--%px*#HrEDw4iYnLwOH5 z{lVLG@}uHDYM}qC%>Nc6?~zZ{!`J0&;2U9I(V+G~t(&|SJ#dd}@?7hi2KKCuEA@w3 zW^K5p%@bzl7x(8GZp*=A(J^T%oN{h(gg+K|@3*{r{LkOtT-WApPScY7Eoal}jYs6N zyt8A!2N(y}<8?O|eOA0Td`CYyFY919aNqXm9K7$TZ5T9P&YS%2%%w)%SAQoTscZF- QVYh3Dck?Ze1zplydng9R* literal 0 HcmV?d00001 diff --git a/bonanza.rc b/bonanza.rc new file mode 100644 index 0000000..27739b1 --- /dev/null +++ b/bonanza.rc @@ -0,0 +1 @@ +"icon" ICON "bonanza.ico" diff --git a/book.c b/book.c new file mode 100644 index 0000000..e8336e6 --- /dev/null +++ b/book.c @@ -0,0 +1,1230 @@ +#include +#include +#include +#include +#include +#include +#include "shogi.h" + +/* + Opening Book Data Structure: Index BookData + + Index: IndexEntry.. (NUM_SECTION times) + + IndexEntry: SectionPointer SectionSize + + BookData: Section.. (NUM_SECTION times) + + Section: [DataEntry].. + + DataEntry: Header Move... + + +- SectionPointer + 4 byte: position of the section in character + +- SectionSize + 2 byte: size of the section in character + +- Header + 1 byte: number of bytes for the DataEntry + 8 byte: hash key + +- Move + 2 byte: a book move + 2 byte: frequency + */ + +#define BK_SIZE_INDEX 6 +#define BK_SIZE_HEADER 9 +#define BK_SIZE_MOVE 4 +#define BK_MAX_MOVE 32 + +#if ( BK_SIZE_HEADER + BK_SIZE_MOVE * BK_MAX_MOVE > UCHAR_MAX ) +# error "Maximum size of DataEntry is larger than UCHAR_MAX" +#endif + +typedef struct { unsigned short smove, freq; } book_move_t; + +typedef struct { int from, to; } ft_t; + +static int book_read( uint64_t key, book_move_t *pbook_move, + unsigned int *pposition ); +static uint64_t book_hash_func( const tree_t * restrict ptree,int *pis_flip ); +static unsigned int bm2move( const tree_t * restrict ptree, unsigned int bmove, + int is_flip ); +static ft_t flip_ft( ft_t ft, int turn, int is_flip ); +static int normalize_book_move( book_move_t * restrict pbook_move, int moves ); + + +int +book_on( void ) +{ + int iret = file_close( pf_book ); + if ( iret < 0 ) { return iret; } + + pf_book = file_open( str_book, "rb+" ); + if ( pf_book == NULL ) { return -2; } + + return 1; +} + + +int +book_off( void ) +{ + int iret = file_close( pf_book ); + if ( iret < 0 ) { return iret; } + + pf_book = NULL; + + return 1; +} + + +int +book_probe( tree_t * restrict ptree ) +{ + book_move_t abook_move[ BK_MAX_MOVE+1 ]; + uint64_t key; + double dscore, drand; + unsigned int move, position, freq_lower_limit; + int is_flip, i, j, moves, ply; + + key = book_hash_func( ptree, &is_flip ); + + moves = book_read( key, abook_move, &position ); + if ( moves <= 0 ) { return moves; } + +#if ! defined(MINIMUM) || ! defined(NDEBUG) + for ( j = i = 0; i < moves; i++ ) { j += abook_move[i].freq; } + if ( j != USHRT_MAX ) + { + str_error = "normalization error (book.bin)"; + return -1; + } +#endif + + /* decision of book move based on pseudo-random number */ + if ( game_status & flag_puzzling ) { j = 0; } + else { + drand = (double)rand64() / (double)UINT64_MAX; + + if ( game_status & flag_narrow_book ) + { +#if defined(BK_ULTRA_NARROW) + freq_lower_limit = abook_move[0].freq; +#else + freq_lower_limit = abook_move[0].freq / 2U; +#endif + + for ( i = 1; i < moves; i++ ) + { + if ( abook_move[i].freq < freq_lower_limit ) { break; } + } + moves = i; + normalize_book_move( abook_move, moves ); + } + + for ( j = moves-1; j > 0; j-- ) + { + dscore = (double)( abook_move[j].freq ) / (double)USHRT_MAX; + if ( drand <= dscore ) { break; } + drand -= dscore; + } + if ( ! abook_move[j].freq ) { j = 0; } + } + + /* show results */ + if ( ! ( game_status & ( flag_pondering | flag_puzzling ) ) ) + { + Out( " move freq\n" ); + OutCsaShogi( "info" ); + for ( i = 0; i < moves; i++ ) + { + const char *str; + + dscore = (double)abook_move[i].freq / (double)USHRT_MAX; + move = bm2move( ptree, (unsigned int)abook_move[i].smove, is_flip ); + str = str_CSA_move( move ); + + Out( " %c %s %5.1f\n", i == j ? '*' : ' ', str, dscore * 100.0 ); + OutCsaShogi( " %s(%.0f%%)", str, dscore * 100.0 ); + } + OutCsaShogi( "\n" ); + } + + move = bm2move( ptree, (unsigned int)abook_move[j].smove, is_flip ); + if ( ! is_move_valid( ptree, move, root_turn ) ) + { + out_warning( "BAD BOOK MOVE!! " ); + return 0; + } + + ply = record_game.moves; + if ( game_status & flag_pondering ) { ply++; } + if ( ply < HASH_REG_HIST_LEN ) + { + history_book_learn[ ply ].key_book = key; + history_book_learn[ ply ].move_probed = move; + history_book_learn[ ply ].key_probed = (unsigned int)HASH_KEY; + history_book_learn[ ply ].hand_probed = HAND_B; + history_book_learn[ ply ].data = (unsigned int)is_flip << 30; + if ( game_status & flag_narrow_book ) + { + history_book_learn[ ply ].data |= 1U << 29; + } + } + + ptree->current_move[1] = move; + + return 1; +} + + +static int +book_read( uint64_t key, book_move_t *pbook_move, unsigned int *pposition ) +{ + uint64_t book_key; + const unsigned char *p; + unsigned int position, size_section, size, u; + int ibook_section, moves; + unsigned short s; + + ibook_section = (int)( (unsigned int)key & (unsigned int)( NUM_SECTION-1 ) ); + + if ( fseek( pf_book, BK_SIZE_INDEX*ibook_section, SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + + if ( fread( &position, sizeof(int), 1, pf_book ) != 1 ) + { + str_error = str_io_error; + return -2; + } + + if ( fread( &s, sizeof(unsigned short), 1, pf_book ) != 1 ) + { + str_error = str_io_error; + return -2; + } + size_section = (unsigned int)s; + if ( size_section > MAX_SIZE_SECTION ) + { + str_error = str_book_error; + return -2; + } + + if ( fseek( pf_book, (long)position, SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + if ( fread( book_section, sizeof(unsigned char), (size_t)size_section, + pf_book ) != (size_t)size_section ) + { + str_error = str_io_error; + return -2; + } + + size = 0; + p = book_section; + *pposition = position; + while ( book_section + size_section > p ) + { + size = (unsigned int)p[0]; + book_key = *(uint64_t *)( p + 1 ); + if ( book_key == key ) { break; } + p += size; + *pposition += size; + } + if ( book_section + size_section <= p ) { return 0; } + + for ( moves = 0, u = BK_SIZE_HEADER; u < size; moves++, u += BK_SIZE_MOVE ) + { + pbook_move[moves].smove = *(unsigned short *)(p+u+0); + pbook_move[moves].freq = *(unsigned short *)(p+u+2); + } + + return moves; +} + + +static ft_t +flip_ft( ft_t ft, int turn, int is_flip ) +{ + int ito_rank, ito_file, ifrom_rank, ifrom_file; + + ito_rank = airank[ft.to]; + ito_file = aifile[ft.to]; + if ( ft.from < nsquare ) + { + ifrom_rank = airank[ft.from]; + ifrom_file = aifile[ft.from]; + } + else { ifrom_rank = ifrom_file = 0; } + + if ( turn ) + { + ito_rank = rank9 - ito_rank; + ito_file = file9 - ito_file; + if ( ft.from < nsquare ) + { + ifrom_rank = rank9 - ifrom_rank; + ifrom_file = file9 - ifrom_file; + } + } + + if ( is_flip ) + { + ito_file = file9 - ito_file; + if ( ft.from < nsquare ) { ifrom_file = file9 - ifrom_file; } + } + + ft.to = ito_rank * nfile + ito_file; + if ( ft.from < nsquare ) { ft.from = ifrom_rank * nfile + ifrom_file; } + + return ft; +} + + +static unsigned int +bm2move( const tree_t * restrict ptree, unsigned int bmove, int is_flip ) +{ + ft_t ft; + unsigned int move; + int is_promote; + + ft.to = I2To(bmove); + ft.from = I2From(bmove); + ft = flip_ft( ft, root_turn, is_flip ); + is_promote = I2IsPromote(bmove); + + move = (unsigned int)( is_promote | From2Move(ft.from) | ft.to ); + if ( ft.from >= nsquare ) { return move; } + + if ( root_turn ) + { + move |= Cap2Move(BOARD[ft.to]); + move |= Piece2Move(-BOARD[ft.from]); + } + else { + move |= Cap2Move(-BOARD[ft.to]); + move |= Piece2Move(BOARD[ft.from]); + } + + return move; +} + + +static uint64_t +book_hash_func( const tree_t * restrict ptree, int *pis_flip ) +{ + uint64_t key, key_flip; + unsigned int hand; + int i, iflip, irank, ifile, piece; + + key = 0; + hand = root_turn ? HAND_W : HAND_B; + i = I2HandPawn(hand); if ( i ) { key ^= b_hand_pawn_rand[i-1]; } + i = I2HandLance(hand); if ( i ) { key ^= b_hand_lance_rand[i-1]; } + i = I2HandKnight(hand); if ( i ) { key ^= b_hand_knight_rand[i-1]; } + i = I2HandSilver(hand); if ( i ) { key ^= b_hand_silver_rand[i-1]; } + i = I2HandGold(hand); if ( i ) { key ^= b_hand_gold_rand[i-1]; } + i = I2HandBishop(hand); if ( i ) { key ^= b_hand_bishop_rand[i-1]; } + i = I2HandRook(hand); if ( i ) { key ^= b_hand_rook_rand[i-1]; } + + hand = root_turn ? HAND_B : HAND_W; + i = I2HandPawn(hand); if ( i ) { key ^= w_hand_pawn_rand[i-1]; } + i = I2HandLance(hand); if ( i ) { key ^= w_hand_lance_rand[i-1]; } + i = I2HandKnight(hand); if ( i ) { key ^= w_hand_knight_rand[i-1]; } + i = I2HandSilver(hand); if ( i ) { key ^= w_hand_silver_rand[i-1]; } + i = I2HandGold(hand); if ( i ) { key ^= w_hand_gold_rand[i-1]; } + i = I2HandBishop(hand); if ( i ) { key ^= w_hand_bishop_rand[i-1]; } + i = I2HandRook(hand); if ( i ) { key ^= w_hand_rook_rand[i-1]; } + + key_flip = key; + + for ( irank = rank1; irank <= rank9; irank++ ) + for ( ifile = file1; ifile <= file9; ifile++ ) + { + if ( root_turn ) + { + i = ( rank9 - irank ) * nfile + file9 - ifile; + iflip = ( rank9 - irank ) * nfile + ifile; + piece = -(int)BOARD[nsquare-i-1]; + } + else { + i = irank * nfile + ifile; + iflip = irank * nfile + file9 - ifile; + piece = (int)BOARD[i]; + } + +#define Foo(t_pc) key ^= (t_pc ## _rand)[i]; \ + key_flip ^= (t_pc ## _rand)[iflip]; + switch ( piece ) + { + case pawn: Foo( b_pawn ); break; + case lance: Foo( b_lance ); break; + case knight: Foo( b_knight ); break; + case silver: Foo( b_silver ); break; + case gold: Foo( b_gold ); break; + case bishop: Foo( b_bishop ); break; + case rook: Foo( b_rook ); break; + case king: Foo( b_king ); break; + case pro_pawn: Foo( b_pro_pawn ); break; + case pro_lance: Foo( b_pro_lance ); break; + case pro_knight: Foo( b_pro_knight ); break; + case pro_silver: Foo( b_pro_silver ); break; + case horse: Foo( b_horse ); break; + case dragon: Foo( b_dragon ); break; + case -pawn: Foo( w_pawn ); break; + case -lance: Foo( w_lance ); break; + case -knight: Foo( w_knight ); break; + case -silver: Foo( w_silver ); break; + case -gold: Foo( w_gold ); break; + case -bishop: Foo( w_bishop ); break; + case -rook: Foo( w_rook ); break; + case -king: Foo( w_king ); break; + case -pro_pawn: Foo( w_pro_pawn ); break; + case -pro_lance: Foo( w_pro_lance ); break; + case -pro_knight: Foo( w_pro_knight ); break; + case -pro_silver: Foo( w_pro_silver ); break; + case -horse: Foo( w_horse ); break; + case -dragon: Foo( w_dragon ); break; + } +#undef Foo + } + + if ( key > key_flip ) + { + key = key_flip; + *pis_flip = 1; + } + else { *pis_flip = 0; } + + return key; +} + + +static int +normalize_book_move( book_move_t * restrict pbook_move, int moves ) +{ + book_move_t swap; + double dscale; + unsigned int u, norm; + int i, j; + + /* insertion sort by nwin */ + pbook_move[moves].freq = 0; + for ( i = moves-2; i >= 0; i-- ) + { + u = pbook_move[i].freq; + swap = pbook_move[i]; + for ( j = i+1; pbook_move[j].freq > u; j++ ) + { + pbook_move[j-1] = pbook_move[j]; + } + pbook_move[j-1] = swap; + } + + /* normalization */ + for ( norm = 0, i = 0; i < moves; i++ ) { norm += pbook_move[i].freq; } + dscale = (double)USHRT_MAX / (double)norm; + for ( norm = 0, i = 0; i < moves; i++ ) + { + u = (unsigned int)( (double)pbook_move[i].freq * dscale ); + if ( ! u ) { u = 1U; } + if ( u > USHRT_MAX ) { u = USHRT_MAX; } + + pbook_move[i].freq = (unsigned short)u; + norm += u; + } + if ( norm > (unsigned int)pbook_move[0].freq + USHRT_MAX ) + { + str_error = "normalization error"; + return -2; + } + + pbook_move[0].freq + = (unsigned short)( pbook_move[0].freq + USHRT_MAX - norm ); + + return 1; +} + + +#if ! defined(MINIMUM) + +#define MaxNumCell 0x400000 +#if defined(BK_SMALL) +# define MaxPlyBook 64 +#else +# define MaxPlyBook 128 +#endif + +typedef struct { + unsigned int nwin, ngame, nwin_bnz, ngame_bnz, move; +} record_move_t; + +typedef struct { + uint64_t key; + unsigned short smove; + unsigned char result; +} cell_t; + +static unsigned int move2bm( unsigned int move, int turn, int is_flip ); +static int find_min_cell( const cell_t *pcell, int ntemp ); +static int read_a_cell( cell_t *pcell, FILE *pf ); +static int CONV_CDECL compare( const void * p1, const void *p2 ); +static int dump_cell( cell_t *pcell, int ncell, int num_tmpfile ); +static int examine_game( tree_t * restrict ptree, record_t *pr, + int *presult, unsigned int *pmoves ); +static int move_selection( const record_move_t *p, int ngame, int nwin ); +static int make_cell_csa( tree_t * restrict ptree, record_t *pr, + cell_t *pcell, int num_tmpfile ); +static int merge_cell( record_move_t *precord_move, FILE **ppf, + int num_tmpfile ); +static int read_anti_book( tree_t * restrict ptree, record_t * pr ); + +int +book_create( tree_t * restrict ptree ) +{ + record_t record; + FILE *ppf[101]; + char str_filename[SIZE_FILENAME]; + record_move_t *precord_move; + cell_t *pcell; + int iret, num_tmpfile, i, j; + + + num_tmpfile = 0; + + pcell = memory_alloc( sizeof(cell_t) * MaxNumCell ); + if ( pcell == NULL ) { return -2; } + + Out("\n [book.csa]\n"); + + iret = record_open( &record, "book.csa", mode_read, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + num_tmpfile = make_cell_csa( ptree, &record, pcell, num_tmpfile ); + if ( num_tmpfile < 0 ) + { + memory_free( pcell ); + record_close( &record ); + return num_tmpfile; + } + + iret = record_close( &record ); + if ( iret < 0 ) + { + memory_free( pcell ); + return iret; + } + + memory_free( pcell ); + + if ( ! num_tmpfile ) + { + str_error = "No book data"; + return -2; + } + + if ( num_tmpfile > 100 ) + { + str_error = "Number of tmp??.bin files are too large."; + return -2; + } + + iret = book_off(); + if ( iret < 0 ) { return iret; } + + pf_book = file_open( str_book, "wb" ); + if ( pf_book == NULL ) { return -2; } + + precord_move = memory_alloc( sizeof(record_move_t) * (MAX_LEGAL_MOVES+1) ); + if ( precord_move == NULL ) { return -2; } + + for ( i = 0; i < num_tmpfile; i++ ) + { + snprintf( str_filename, SIZE_FILENAME, "tmp%02d.bin", i ); + ppf[i] = file_open( str_filename, "rb" ); + if ( ppf[i] == NULL ) + { + memory_free( precord_move ); + file_close( pf_book ); + for ( j = 0; j < i; j++ ) { file_close( ppf[j] ); } + return -2; + } + } + + iret = merge_cell( precord_move, ppf, num_tmpfile ); + if ( iret < 0 ) + { + memory_free( precord_move ); + file_close( pf_book ); + for ( i = 0; i < num_tmpfile; i++ ) { file_close( ppf[i] ); } + return iret; + } + + memory_free( precord_move ); + + iret = book_on(); + if ( iret < 0 ) { return iret; } + +#if 1 + iret = record_open( &record, "book_anti.csa", mode_read, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + iret = read_anti_book( ptree, &record ); + if ( iret < 0 ) + { + record_close( &record ); + return iret; + } +#endif + + return record_close( &record ); +} + + +static int +read_anti_book( tree_t * restrict ptree, record_t * pr ) +{ + uint64_t key; + book_move_t abook_move[ BK_MAX_MOVE+1 ]; + size_t size; + unsigned int move, position, bm, umoves; + int iret, result, istatus, is_flip, i, moves; + + do { + + istatus = examine_game( ptree, pr, &result, &umoves ); + if ( istatus < 0 ) { return istatus; } + if ( result == -2 ) + { + str_error = "no result in book_anti.csa"; + return -2; + } + + while ( pr->moves < umoves-1U ) + { + istatus = in_CSA( ptree, pr, NULL, 0 ); + if ( istatus != record_misc ) + { + str_error = "internal error at book.c"; + return -2; + } + } + + istatus = in_CSA( ptree, pr, &move, flag_nomake_move ); + if ( istatus < 0 ) { return istatus; } + + key = book_hash_func( ptree, &is_flip ); + + moves = book_read( key, abook_move, &position ); + if ( moves < 0 ) { return moves; } + + bm = move2bm( move, root_turn, is_flip ); + for ( i = 0; i < moves; i++ ) + { + if ( bm == abook_move[i].smove ) { break; } + } + + if ( i == moves ) + { + out_board( ptree, stdout, 0, 0 ); + printf( "%s is not found in the book\n\n", str_CSA_move(move) ); + } + else { + abook_move[i].freq = 0; + + iret = normalize_book_move( abook_move, moves ); + if ( iret < 0 ) { return iret; } + + for ( i = 0; i < moves; i++ ) + { + *(unsigned short *)( book_section + i*BK_SIZE_MOVE ) + = abook_move[i].smove; + *(unsigned short *)( book_section + i*BK_SIZE_MOVE + 2 ) + = abook_move[i].freq; + } + size = (size_t)( moves * BK_SIZE_MOVE ); + if ( fseek( pf_book, (long)(position+BK_SIZE_HEADER), SEEK_SET ) == EOF + || fwrite( book_section, sizeof(unsigned char), + size, pf_book ) != size ) + { + str_error = str_io_error; + return -2; + } + + out_board( ptree, stdout, 0, 0 ); + printf( "%s is discarded\n\n", str_CSA_move(move) ); + } + + if ( istatus != record_eof && istatus != record_next ) + { + istatus = record_wind( pr ); + if ( istatus < 0 ) { return istatus; } + } + } while ( istatus != record_eof ); + + return 1; +} + + +static int +make_cell_csa( tree_t * restrict ptree, record_t *pr, cell_t *pcell, + int num_tmpfile ) +{ + struct { + uint64_t hash_key; + unsigned int hand, move; + } rep_tbl[MaxPlyBook+1]; + uint64_t key; + unsigned int nwhite_win, nblack_win, ndraw, ninvalid, nbnz_black, nbnz_white; + unsigned int move, moves, uresult; + int icell, result, is_flip, iret, istatus, ply, i, black_bnz, white_bnz; + + nwhite_win = nblack_win = ndraw = ninvalid = nbnz_white = nbnz_black = 0; + icell = black_bnz = white_bnz = 0; + istatus = record_next; + + while ( istatus != record_eof ) { + + istatus = examine_game( ptree, pr, &result, &moves ); + if ( istatus < 0 ) { return istatus; } + + if ( result == -1 ) { nwhite_win++; } + else if ( result == 1 ) { nblack_win++; } + else if ( result == 0 ) { ndraw++; } + else { + ninvalid++; + continue; + } + + if ( moves > MaxPlyBook ) { moves = MaxPlyBook; } + + for ( ply = 0;; ply++ ) { + istatus = in_CSA( ptree, pr, &move, flag_nomake_move ); + if ( ! ply ) + { + black_bnz = strcmp( pr->str_name1, "Bonanza" ) ? 0 : 1; + white_bnz = strcmp( pr->str_name2, "Bonanza" ) ? 0 : 1; + if ( ! strcmp( pr->str_name1, "Bonanza" ) ) + { + black_bnz = 1; + nbnz_black += 1; + } + else { black_bnz = 0; } + if ( ! strcmp( pr->str_name2, "Bonanza" ) ) + { + white_bnz = 1; + nbnz_white += 1; + } + else { white_bnz = 0; } + } + if ( istatus < 0 ) { return istatus; } + if ( istatus == record_resign && ! moves ) { break; } + if ( istatus != record_misc ) + { + str_error = "internal error at book.c"; + return -2; + } + + rep_tbl[ply].hash_key = HASH_KEY; + rep_tbl[ply].hand = HAND_B; + rep_tbl[ply].move = move; + for ( i = ( ply & 1 ); i < ply; i += 2 ) + { + if ( rep_tbl[i].hash_key == HASH_KEY + && rep_tbl[i].hand == HAND_B + && rep_tbl[i].move == move ) { break; } + } + + if ( i == ply ) { + key = book_hash_func( ptree, &is_flip ); + uresult = (unsigned int)( root_turn ? -1*result+1 : result+1 ); + if ( ( root_turn == black && black_bnz ) + || ( root_turn == white && white_bnz ) ) { uresult |= 0x4U; } + + pcell[icell].key = key; + pcell[icell].result = (unsigned char)uresult; + pcell[icell].smove = (unsigned short)move2bm( move, root_turn, + is_flip ); + icell++; + if ( icell == MaxNumCell ) { + iret = dump_cell( pcell, icell, num_tmpfile++ ); + if ( iret < 0 ) { return iret; } + icell = 0; + } + if ( ! ( (icell-1) & 0x1ffff ) ) { Out( "." ); } + } + + if ( pr->moves >= moves ) { break; } + + iret = make_move_root( ptree, move, 0 ); + if ( iret < 0 ) { return iret; } + } + + if ( istatus != record_eof && istatus != record_next ) + { + istatus = record_wind( pr ); + if ( istatus < 0 ) { return istatus; } + } + } + + iret = dump_cell( pcell, icell, num_tmpfile++ ); + if ( iret < 0 ) { return iret; } + + Out( "\n" + "Total games: %7u\n" + " Discarded: %7u\n" + " Black wins: %7u\n" + " White wins: %7u\n" + " Drawn: %7u\n" + " Black Bnz: %7u\n" + " White Bnz: %7u\n", nblack_win + nwhite_win + ndraw + ninvalid, + ninvalid, nblack_win, nwhite_win, ndraw, nbnz_black, nbnz_white ); + + return num_tmpfile; +} + + +static int +merge_cell( record_move_t *precord_move, FILE **ppf, int num_tmpfile ) +{ + double dscale; + record_move_t swap; + cell_t acell[101]; + uint64_t key; + unsigned int book_moves, book_positions, move, size_data, size_section; + unsigned int max_size_section, nwin, nwin_bnz, ngame, ngame_bnz, position; + unsigned int u, norm; + int i, j, iret, ibook_section, imin, nmove; + unsigned short s; + + for ( i = 0; i < num_tmpfile; i++ ) + { + iret = read_a_cell( acell + i, ppf[i] ); + if ( iret < 0 ) { return iret; } + } + + imin = find_min_cell( acell, num_tmpfile ); + position = BK_SIZE_INDEX * NUM_SECTION; + max_size_section = book_moves = book_positions = 0; + for ( ibook_section = 0; ibook_section < NUM_SECTION; ibook_section++ ) { + size_section = 0; + for (;;) { + key = acell[imin].key; + i = (int)( (unsigned int)key & (unsigned int)(NUM_SECTION-1) ); + if ( i != ibook_section || key == UINT64_MAX ) { break; } + + nwin = nmove = nwin_bnz = ngame = ngame_bnz = precord_move[0].move = 0; + do { + move = (unsigned int)acell[imin].smove; + for ( i = 0; precord_move[i].move && precord_move[i].move != move; + i++ ); + if ( ! precord_move[i].move ) + { + precord_move[i].nwin = precord_move[i].ngame = 0; + precord_move[i].nwin_bnz = precord_move[i].ngame_bnz = 0; + precord_move[i].move = move; + precord_move[i+1].move = 0; + nmove++; + } + + if ( acell[imin].result & b0010 ) + { + if ( acell[imin].result & b0100 ) + { + precord_move[i].nwin_bnz += 1; + nwin_bnz += 1; + } + precord_move[i].nwin += 1; + nwin += 1; + } + + if ( acell[imin].result & b0100 ) + { + precord_move[i].ngame_bnz += 1; + ngame_bnz += 1; + } + precord_move[i].ngame += 1; + ngame += 1; + + iret = read_a_cell( acell + imin, ppf[imin] ); + if ( iret < 0 ) { return iret; } + + imin = find_min_cell( acell, num_tmpfile ); + } while ( key == acell[imin].key ); + +#if defined(BK_COM) + while ( nmove > 1 && ngame_bnz >= 128 ) + { + double max_rate, rate; + + max_rate = 0.0; + for ( i = 0; i < nmove; i++ ) + { + rate = ( (double)precord_move[i].nwin_bnz + / (double)( precord_move[i].ngame_bnz + 7 ) ); + if ( rate > max_rate ) { max_rate = rate; } + } + if ( max_rate < 0.1 ) { break; } + + max_rate *= 0.85; + i = 0; + do { + rate = ( (double)precord_move[i].nwin_bnz + / (double)( precord_move[i].ngame_bnz + 7 ) ); + + if ( rate > max_rate ) { i++; } + else { + precord_move[i] = precord_move[nmove-1]; + nmove -= 1; + } + } while ( i < nmove ); + + break; + } +#endif + if ( ! nmove ) { continue; } + + i = 0; + do { + if ( move_selection( precord_move + i, ngame, nwin ) ) { i++; } + else { + precord_move[i] = precord_move[nmove-1]; + nmove -= 1; + } + } while ( i < nmove ); + + if ( ! nmove ) { continue; } + + size_data = BK_SIZE_HEADER + BK_SIZE_MOVE * nmove; + if ( size_section + size_data > MAX_SIZE_SECTION + || size_data > UCHAR_MAX ) + { + str_error = "book_section buffer overflow"; + return -2; + } + if ( nmove > BK_MAX_MOVE ) + { + str_error = "BK_MAX_MOVE is too small"; + return -2; + } + + /* insertion sort by nwin */ + precord_move[nmove].nwin = 0; + for ( i = nmove-2; i >= 0; i-- ) + { + u = precord_move[i].nwin; + swap = precord_move[i]; + for ( j = i+1; precord_move[j].nwin > u; j++ ) + { + precord_move[j-1] = precord_move[j]; + } + precord_move[j-1] = swap; + } + + /* normalize nwin */ + for ( norm = 0, i = 0; i < nmove; i++ ) { norm += precord_move[i].nwin; } + dscale = (double)USHRT_MAX / (double)norm; + for ( norm = 0, i = 0; i < nmove; i++ ) + { + u = (unsigned int)( (double)precord_move[i].nwin * dscale ); + if ( ! u ) { u = 1U; } + if ( u > USHRT_MAX ) { u = USHRT_MAX; } + + precord_move[i].nwin = u; + norm += u; + } + if ( norm > precord_move[0].nwin + USHRT_MAX ) + { + str_error = "normalization error\n"; + return -2; + } + precord_move[0].nwin += USHRT_MAX - norm; + + book_section[size_section+0] = (unsigned char)size_data; + *(uint64_t *)(book_section+size_section+1) = key; + + for ( u = size_section+BK_SIZE_HEADER, i = 0; i < nmove; + u += BK_SIZE_MOVE, i++ ) + { + *(unsigned short *)(book_section+u) + = (unsigned short)precord_move[i].move; + *(unsigned short *)(book_section+u+2) + = (unsigned short)precord_move[i].nwin; + } + book_positions += 1; + book_moves += nmove; + size_section += size_data; + } + if ( fseek( pf_book, BK_SIZE_INDEX * ibook_section, SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + if ( fwrite( &position, sizeof(unsigned int), 1, pf_book ) != 1 ) + { + str_error = str_io_error; + return -2; + } + s = (unsigned short)size_section; + if ( fwrite( &s, sizeof(unsigned short), 1, pf_book ) != 1 ) + { + str_error = str_io_error; + return -2; + } + if ( fseek( pf_book, position, SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + if ( fwrite( &book_section, sizeof(unsigned char), (size_t)size_section, + pf_book ) != (size_t)size_section ) + { + str_error = str_io_error; + return -2; + } + + if ( size_section > max_size_section ) { max_size_section = size_section; } + position += size_section; + } + + Out( "Positions in the book: %u\n", book_positions ); + Out( "Moves in the book: %u\n", book_moves ); + Out( "Max. size of a section: %d\n", max_size_section ); + + return 1; +} + + +static int +move_selection( const record_move_t *p, int ngame, int nwin ) +{ + double total_win_norm, win_norm, win, game, win_move, game_move; + +#if defined(BK_SMALL) + if ( ! p->nwin || p->ngame < 3 ) { return 0; } +#else + if ( ! p->nwin || p->ngame < 2 ) { return 0; } +#endif + + win = (double)nwin; + game = (double)ngame; + win_move = (double)p->nwin; + game_move = (double)p->ngame; + + total_win_norm = win * game_move; + win_norm = win_move * game; + if ( win_norm < total_win_norm * 0.85 ) { return 0; } + + return 1; +} + + +static int +find_min_cell( const cell_t *pcell, int num_tmpfile ) +{ + int imin, i; + + imin = 0; + for ( i = 1; i < num_tmpfile; i++ ) + { + if ( compare( pcell+imin, pcell+i ) == 1 ) { imin = i; } + } + return imin; +} + + +static int +read_a_cell( cell_t *pcell, FILE *pf ) +{ + if ( fread( &pcell->key, sizeof(uint64_t), 1, pf ) != 1 ) + { + if ( feof( pf ) ) + { + pcell->key = UINT64_MAX; + return 1; + } + str_error = str_io_error; + return -2; + } + if ( fread( &pcell->smove, sizeof(unsigned short), 1, pf ) != 1 ) + { + str_error = str_io_error; + return -2; + } + if ( fread( &pcell->result, sizeof(unsigned char), 1, pf ) != 1 ) + { + str_error = str_io_error; + return -2; + } + + return 1; +} + + +static int +examine_game( tree_t * restrict ptree, record_t *pr, int *presult, + unsigned int *pmoves ) +{ + rpos_t rpos; + int iret, istatus, is_lost, is_win; + unsigned int moves; + + *presult = -2; + + iret = record_getpos( pr, &rpos ); + if ( iret < 0 ) { return iret; } + + is_lost = is_win = 0; + moves = 0; + do { + istatus = in_CSA( ptree, pr, NULL, flag_detect_hang ); + if ( istatus < 0 ) + { + /* the game is end, however the record is invalid */ + if ( strstr( str_error, str_bad_record ) != NULL + && ( game_status & mask_game_end ) ) + { + break; + } + + /* a hang-king and a double-pawn are counted as a lost game */ + if ( strstr( str_error, str_king_hang ) != NULL + || strstr( str_error, str_double_pawn ) != NULL + || strstr( str_error, str_mate_drppawn ) != NULL ) + { + is_lost = 1; + break; + } + + return istatus; + } + /* previous move had an error, count as a won game */ + else if ( istatus == record_error ) + { + is_win = 1; + break; + } + else if ( istatus == record_misc ) { moves++; } + } while ( istatus != record_next && istatus != record_eof ); + + if ( istatus != record_next && istatus != record_eof ) + { + istatus = record_wind( pr ); + if ( istatus < 0 ) { return istatus; } + } + + if ( ! ( is_lost || is_win || ( game_status & mask_game_end ) ) ) + { + return istatus; + } + + if ( is_win ) { *presult = root_turn ? -1 : 1; } + else if ( is_lost || ( game_status & ( flag_mated | flag_resigned ) ) ) + { + *presult = root_turn ? 1 : -1; + } + else { *presult = 0; } + + *pmoves = moves; + + iret = record_setpos( pr, &rpos ); + if ( iret < 0 ) { return iret; } + + return istatus; +} + + +static int +dump_cell( cell_t *pcell, int ncell, int num_tmpfile ) +{ + char str_filename[SIZE_FILENAME]; + FILE *pf; + int i, iret; + + Out( " sort" ); + qsort( pcell, ncell, sizeof(cell_t), compare ); + + Out( " dump", str_filename ); + snprintf( str_filename, SIZE_FILENAME, "tmp%02d.bin", num_tmpfile ); + pf = file_open( str_filename, "wb" ); + if ( pf == NULL ) { return -2; } + + for ( i = 0; i < ncell; i++ ) + { + if ( fwrite( &pcell[i].key, sizeof(uint64_t), 1, pf ) != 1 ) + { + file_close( pf ); + str_error = str_io_error; + return -2; + } + if ( fwrite( &pcell[i].smove, sizeof(unsigned short), 1, pf ) != 1 ) + { + file_close( pf ); + str_error = str_io_error; + return -2; + } + if ( fwrite( &pcell[i].result, sizeof(unsigned char), 1, pf ) != 1 ) + { + file_close( pf ); + str_error = str_io_error; + return -2; + } + } + + iret = file_close( pf ); + if ( iret < 0 ) { return iret; } + + Out( " done (%s)\n", str_filename ); + + return 1; +} + + +static int CONV_CDECL +compare( const void * p1, const void * p2 ) +{ + const cell_t * pcell1 = p1; + const cell_t * pcell2 = p2; + unsigned int u1, u2; + + u1 = (unsigned int)pcell1->key & (unsigned int)(NUM_SECTION-1); + u2 = (unsigned int)pcell2->key & (unsigned int)(NUM_SECTION-1); + + if ( u1 < u2 ) { return -1; } + if ( u1 > u2 ) { return 1; } + if ( pcell1->key < pcell2->key ) { return -1; } + if ( pcell1->key > pcell2->key ) { return 1; } + + return 0; +} + + +static unsigned int +move2bm( unsigned int move, int turn, int is_flip ) +{ + ft_t ft; + unsigned int bmove; + int is_promote; + + ft.to = I2To(move); + ft.from = I2From(move); + is_promote = I2IsPromote(move); + + ft = flip_ft( ft, turn, is_flip ); + + bmove = (unsigned int)( is_promote | From2Move(ft.from) | ft.to ); + + return bmove; +} + + +#endif /* no MINIMUM */ diff --git a/book_anti.csa b/book_anti.csa new file mode 100644 index 0000000..22d1789 --- /dev/null +++ b/book_anti.csa @@ -0,0 +1,6 @@ +PI, +, +6978KI, -8384FU, %TORYO, / +PI, +, +7776FU, -4132KI, +2726FU, %TORYO, / +PI, +, +7776FU, -4132KI, +6978KI, %TORYO, / +PI, +, +7776FU, -7162GI, -6766FU, %TORYO, / +PI, +, +7776FU, -3334FU, +3948GI, -4344FU, %TORYO, / +PI, +, +5968OU, -3334FU, %TORYO diff --git a/csa.c b/csa.c new file mode 100644 index 0000000..e99f953 --- /dev/null +++ b/csa.c @@ -0,0 +1,965 @@ +#include +#include +#include +#include +#include +#include +#include +#include "shogi.h" + +static void out_CSA_header( const tree_t * restrict ptree, record_t *pr ); +static int str2piece( const char *str ); +static int skip_comment( record_t *pr ); +static int read_char( record_t *pr ); +static int read_CSA_line( record_t *pr, char *str ); +static int in_CSA_header( tree_t * restrict ptree, record_t *pr, int flag ); +static int read_board_rep2( const char *str_line, min_posi_t *pmin_posi ); +static int read_board_rep3( const char *str_line, min_posi_t *pmin_posi ); + +int +read_record( tree_t * restrict ptree, const char *str_file, + unsigned int moves, int flag ) +{ + record_t record; + int iret; + + iret = record_open( &record, str_file, mode_read, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + if ( ! moves ) + { + iret = in_CSA_header( ptree, &record, flag ); + if ( iret < 0 ) + { + record_close( &record ); + return iret; + } + } + else do { + iret = in_CSA( ptree, &record, NULL, flag ); + if ( iret < 0 ) + { + record_close( &record ); + return iret; + } + } while ( iret != record_next + && iret != record_eof + && moves > record.moves ); + + return record_close( &record ); +} + + +int +record_open( record_t *pr, const char *str_file, record_mode_t record_mode, + const char *str_name1, const char *str_name2 ) +{ + pr->games = pr->moves = pr->lines = 0; + pr->str_name1[0] = '\0'; + pr->str_name2[0] = '\0'; + + if ( str_name1 ) + { + strncpy( pr->str_name1, str_name1, SIZE_PLAYERNAME-1 ); + pr->str_name1[SIZE_PLAYERNAME-1] = '\0'; + } + + if ( str_name2 ) + { + strncpy( pr->str_name2, str_name2, SIZE_PLAYERNAME-1 ); + pr->str_name2[SIZE_PLAYERNAME-1] = '\0'; + } + + if ( record_mode == mode_write ) + { + pr->pf = file_open( str_file, "w" ); + if ( pr->pf == NULL ) { return -2; } + } + else if ( record_mode == mode_read_write ) + { + pr->pf = file_open( str_file, "wb+" ); + if ( pr->pf == NULL ) { return -2; } + } + else { + assert( record_mode == mode_read ); + + pr->pf = file_open( str_file, "rb" ); + if ( pr->pf == NULL ) { return -2; } + } + + return 1; +} + + +int +record_close( record_t *pr ) +{ + int iret = file_close( pr->pf ); + pr->pf = NULL; + return iret; +} + + +void +out_CSA( tree_t * restrict ptree, record_t *pr, unsigned int move ) +{ + const char *str_move; + unsigned int sec; + + /* print move */ + if ( move == MOVE_RESIGN ) + { + if ( ! pr->moves ) { out_CSA_header( ptree, pr ); } + fprintf( pr->pf, "%s\n", str_resign ); + pr->lines++; + } + else { + if ( ! pr->moves ) + { + root_turn = Flip(root_turn); + UnMakeMove( root_turn, move, 1 ); + out_CSA_header( ptree, pr ); + MakeMove( root_turn, move, 1 ); + root_turn = Flip(root_turn); + } + str_move = str_CSA_move( move ); + fprintf( pr->pf, "%c%s\n", ach_turn[Flip(root_turn)], str_move ); + pr->lines++; + pr->moves++; + } + + /* print time */ + sec = root_turn ? sec_b_total : sec_w_total; + + fprintf( pr->pf, "T%-7u,'%03u:%02u \n", sec_elapsed, sec / 60U, sec % 60U ); + pr->lines++; + + /* print repetition or mate status */ + if ( game_status & flag_mated ) + { + fprintf( pr->pf, "%%TSUMI\n" ); + pr->lines++; + } + else if ( game_status & flag_drawn ) + { + fprintf( pr->pf, "%s\n", str_repetition ); + pr->lines++; + } + + fflush( pr->pf ); +} + + +int +record_wind( record_t *pr ) +{ + char str_line[ SIZE_CSALINE ]; + int iret; + for (;;) + { + iret = read_CSA_line( pr, str_line ); + if ( iret < 0 ) { return iret; } + if ( ! iret ) { return record_eof; } + if ( ! strcmp( str_line, "/" ) ) { break; } + } + pr->games++; + pr->moves = 0; + return record_next; +} + + +#if ! defined(MINIMUM) +int +record_rewind( record_t *pr ) +{ + pr->games = pr->moves = pr->lines = 0; + if ( fseek( pr->pf, 0, SEEK_SET ) ) { return -2; } + + return 1; +} + + +int +record_getpos( record_t *pr, rpos_t *prpos ) +{ + if ( fgetpos( pr->pf, &prpos->fpos ) ) + { + str_error = "fgetpos() failed."; + return -2; + } + prpos->games = pr->games; + prpos->moves = pr->moves; + prpos->lines = pr->lines; + + return 1; +} + + +int +record_setpos( record_t *pr, const rpos_t *prpos ) +{ + if ( fsetpos( pr->pf, &prpos->fpos ) ) + { + str_error = "fsetpos() failed."; + return -2; + } + pr->games = prpos->games; + pr->moves = prpos->moves; + pr->lines = prpos->lines; + + return 1; +} +#endif /* no MINIMUM */ + + +int +in_CSA( tree_t * restrict ptree, record_t *pr, unsigned int *pmove, int flag ) +{ + char str_line[ SIZE_CSALINE ]; + char *ptr; + unsigned int move; + long l; + int iret; + + if ( pr->moves == 0 ) + { + iret = in_CSA_header( ptree, pr, flag ); + if ( iret < 0 ) { return iret; } + } + + do { + iret = read_CSA_line( pr, str_line ); + if ( iret < 0 ) { return iret; } + if ( ! iret ) { return record_eof; } + if ( ! strcmp( str_line, str_resign ) ) + { + game_status |= flag_resigned; + return record_resign; + } + if ( ! strcmp( str_line, str_repetition ) + || ! strcmp( str_line, str_jishogi ) ) + { + game_status |= flag_drawn; + return record_drawn; + } + if ( ! strcmp( str_line, str_record_error ) ) + { + return record_error; + } + } while ( str_line[0] == 'T' || str_line[0] == '%' ); + + if ( ! strcmp( str_line, "/" ) ) + { + pr->games++; + pr->moves = 0; + return record_next; + } + + if ( game_status & mask_game_end ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_bad_record ); + str_error = str_message; + return -2; + } + + iret = interpret_CSA_move( ptree, &move, str_line+1 ); + if ( iret < 0 ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_error ); + str_error = str_message; + return -2; + } + if ( pmove != NULL ) { *pmove = move; } + + /* do time */ + if ( flag & flag_time ) + { + iret = read_CSA_line( pr, str_line ); + if ( iret < 0 ) { return iret; } + if ( ! iret ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_unexpect_eof ); + str_error = str_message; + return -2; + } + if ( str_line[0] != 'T' ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, pr->lines, + "Time spent is not available." ); + str_error = str_message; + return -2; + } + l = strtol( str_line+1, &ptr, 0 ); + if ( ptr == str_line+1 || l == LONG_MAX || l < 0 ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_bad_record ); + str_error = str_message; + return -2; + } + } + else { l = 0; } + sec_elapsed = (unsigned int)l; + if ( root_turn ) { sec_w_total += (unsigned int)l; } + else { sec_b_total += (unsigned int)l; } + + iret = make_move_root( ptree, move, flag & ~flag_time ); + if ( iret < 0 ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_error ); + str_error = str_message; + return iret; + } + + pr->moves++; + + return record_misc; +} + + +int +interpret_CSA_move( tree_t * restrict ptree, unsigned int *pmove, + const char *str ) +{ + int ifrom_file, ifrom_rank, ito_file, ito_rank, ipiece; + int ifrom, ito; + unsigned int move; + unsigned int *pmove_last; + unsigned int *p; + + ifrom_file = str[0]-'0'; + ifrom_rank = str[1]-'0'; + ito_file = str[2]-'0'; + ito_rank = str[3]-'0'; + + ito_file = 9 - ito_file; + ito_rank = ito_rank - 1; + ito = ito_rank * 9 + ito_file; + ipiece = str2piece( str+4 ); + if ( ipiece < 0 ) + { + str_error = str_illegal_move; + return -2; + } + + if ( ! ifrom_file && ! ifrom_rank ) + { + move = To2Move(ito) | Drop2Move(ipiece); + ifrom = nsquare; + } + else { + ifrom_file = 9 - ifrom_file; + ifrom_rank = ifrom_rank - 1; + ifrom = ifrom_rank * 9 + ifrom_file; + if ( abs(BOARD[ifrom]) + promote == ipiece ) + { + ipiece -= promote; + move = FLAG_PROMO; + } + else { move = 0; } + + move |= ( To2Move(ito) | From2Move(ifrom) | Cap2Move(abs(BOARD[ito])) + | Piece2Move(ipiece) ); + } + + *pmove = 0; + pmove_last = ptree->amove; + pmove_last = GenCaptures(root_turn, pmove_last ); + pmove_last = GenNoCaptures(root_turn, pmove_last ); + pmove_last = GenCapNoProEx2(root_turn, pmove_last ); + pmove_last = GenNoCapNoProEx2(root_turn, pmove_last ); + pmove_last = GenDrop( root_turn, pmove_last ); + for ( p = ptree->amove; p < pmove_last; p++ ) + { + if ( *p == move ) + { + *pmove = move; + break; + } + } + + if ( ! *pmove ) + { + str_error = str_illegal_move; + if ( ipiece == pawn + && ifrom == nsquare + && ! BOARD[ito] + && ( root_turn ? IsHandPawn(HAND_W) : IsHandPawn(HAND_B) ) ) + { + unsigned int u; + + if ( root_turn ) + { + u = BBToU( BB_WPAWN_ATK ); + if ( u & (mask_file1>>ito_file) ) + { + str_error = str_double_pawn; + } + else if ( BOARD[ito+nfile] == king ) + { + str_error = str_mate_drppawn; + } + } + else { + u = BBToU( BB_BPAWN_ATK ); + if ( u & (mask_file1>>ito_file) ) { str_error = str_double_pawn; } + else if ( BOARD[ito-nfile] == -king ) + { + str_error = str_mate_drppawn; + } + } + } + return -2; + } + + return 1; +} + + +const char * +str_CSA_move_plus( tree_t * restrict ptree, unsigned int move, int ply, + int turn ) +{ + static char str[ 13 ]; + const unsigned int *pmove_last; + unsigned int amove[ MAX_LEGAL_EVASION ]; + char *p; + int is_promo, ipiece_cap, ipiece_move, ifrom, ito, turn_next; + + is_promo = (int)I2IsPromote(move); + ipiece_move = (int)I2PieceMove(move); + ifrom = (int)I2From(move); + ito = (int)I2To(move); + ipiece_cap = (int)UToCap(move); + turn_next = Flip( turn ); + + if ( is_promo && ipiece_cap ) + { + snprintf( str, 13, "%d%d%d%d%spx%s", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move + promote ], + astr_table_piece[ ipiece_cap ] ); + p = str + 10; + } + else if ( ipiece_cap ) + { + snprintf( str, 13, "%d%d%d%d%sx%s", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move ], + astr_table_piece[ ipiece_cap ] ); + p = str + 9; + } + else if ( is_promo ) + { + snprintf( str, 13, "%d%d%d%d%sp", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move + promote ] ); + p = str + 7; + } + else if ( ifrom < nsquare ) + { + snprintf( str, 13, "%d%d%d%d%s", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move ] ); + p = str + 6; + } + else { + snprintf( str, 13, "00%d%d%s", 9-aifile[ito], airank[ito]+1, + astr_table_piece[ From2Drop(ifrom) ] ); + p = str + 6; + } + + MakeMove( turn, move, ply ); + if ( InCheck( turn_next ) ) + { + pmove_last = GenEvasion( turn_next, amove ); + if ( pmove_last == amove ) { *p++ = '#'; } + else { *p++ = '!'; } + *p = '\0'; + } + UnMakeMove( turn, move, ply ); + + return str; +} + + +const char * +str_CSA_move( unsigned int move ) +{ + static char str[7]; + int ifrom, ito, ipiece_move, is_promote; + + is_promote = (int)I2IsPromote(move); + ipiece_move = (int)I2PieceMove(move); + ifrom = (int)I2From(move); + ito = (int)I2To(move); + + if ( is_promote ) + { + snprintf( str, 7, "%d%d%d%d%s", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move + promote ] ); + } + else if ( ifrom < nsquare ) + { + snprintf( str, 7, "%d%d%d%d%s", + 9-aifile[ifrom], airank[ifrom]+1, + 9-aifile[ito], airank[ito] +1, + astr_table_piece[ ipiece_move ] ); + } + else { + snprintf( str, 7, "00%d%d%s", + 9-aifile[ito], airank[ito]+1, + astr_table_piece[ From2Drop(ifrom) ] ); + } + + return str; +} + + +int +read_board_rep1( const char *str_line, min_posi_t *pmin_posi ) +{ + const char *p; + char str_piece[3]; + int piece, ifile, irank, isquare; + signed char board[nsquare]; + + memcpy( board, &min_posi_no_handicap.asquare, nsquare ); + + for ( p = str_line + 2; p[0] != '\0'; p += 4 ) + { + if ( p[1] == '\0' || p[2] == '\0' || p[3] == '\0' ) + { + str_error = str_bad_board; + return -2; + } + str_piece[0] = p[2]; + str_piece[1] = p[3]; + str_piece[2] = '\0'; + piece = str2piece( str_piece ); + ifile = p[0]-'0'; + irank = p[1]-'0'; + ifile = 9 - ifile; + irank = irank - 1; + isquare = irank * nfile + ifile; + if ( piece == -2 || ifile < file1 || ifile > file9 || irank < rank1 + || irank > rank9 || abs(board[isquare]) != piece ) + { + str_error = str_bad_board; + return -2; + } + board[isquare] = empty; + } + + for ( isquare = 0; isquare < nsquare; isquare++ ) if ( board[isquare] ) + { + if ( pmin_posi->asquare[isquare] ) + { + str_error = str_bad_board; + return -2; + } + pmin_posi->asquare[isquare] = board[isquare]; + } + + return 1; +} + + +static void +out_CSA_header( const tree_t * restrict ptree, record_t *pr ) +{ + time_t t; + + fprintf( pr->pf, "'Bonanza version " BNZ_VER "\n" ); + + if ( pr->str_name1[0] != '\0' ) + { + fprintf( pr->pf, "N+%s\n", pr->str_name1 ); + } + + if ( pr->str_name2[0] != '\0' ) + { + fprintf( pr->pf, "N-%s\n", pr->str_name2 ); + } + + t = time( NULL ); + if ( t == (time_t)-1 ) { out_warning( "%s time() faild." ); } + else { +#if defined(_MSC_VER) + struct tm tm; + localtime_s( &tm, &t ); + fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n", + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec ); +#else + struct tm *ptm; + ptm = localtime( &t ); + fprintf( pr->pf, "$START_TIME:%4d/%02d/%02d %02d:%02d:%02d\n", + ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, + ptm->tm_hour, ptm->tm_min, ptm->tm_sec ); +#endif + } + + if ( ! memcmp( BOARD, min_posi_no_handicap.asquare, nsquare ) + && min_posi_no_handicap.turn_to_move == root_turn + && min_posi_no_handicap.hand_black == HAND_B + && min_posi_no_handicap.hand_white == HAND_W ) + { + fprintf( pr->pf, "PI\n" ); + pr->lines++; + } + else { + out_board( ptree, pr->pf, 0, 1 ); + pr->lines += 10; + } + + if ( root_turn ) { fprintf( pr->pf, "-\n" ); } + else { fprintf( pr->pf, "+\n" ); } + pr->lines++; +} + + +static int +in_CSA_header( tree_t * restrict ptree, record_t *pr, int flag ) +{ + min_posi_t min_posi; + const char *str_name1, *str_name2; + char str_line[ SIZE_CSALINE ]; + int iret, is_rep1_done, is_rep2_done, is_all_done, i, j; + + for ( i = 0; i < MAX_ANSWER; i++ ) { pr->info.str_move[i][0] = '\0'; } + str_name1 = str_name2 = NULL; + + /* version and info */ + for ( ;; ) + { + iret = read_CSA_line( pr, str_line ); + if ( iret < 0 ) { return iret; } + + if ( str_line[0] != 'N' + && str_line[0] != 'V' + && str_line[0] != '$' ) { break; } + + if ( ! memcmp( str_line, "$ANSWER:", 8 ) ) + { + for ( i = 0; i < MAX_ANSWER; i++ ) + { + for ( j = 0; j < 8; j++ ) + { + pr->info.str_move[i][j] = str_line[8+i*8+j]; + } + pr->info.str_move[i][7] = '\0'; + if ( str_line[8+i*8+7] == '\0' ) { break; } + } + if ( i == MAX_ANSWER ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, pr->lines, + "The number of answers reached MAX_ANSWER." ); + str_error = str_message; + return -2; + } + } + else if ( ! memcmp( str_line, "N+", 2 ) ) + { + strncpy( pr->str_name1, str_line+2, SIZE_PLAYERNAME-1 ); + pr->str_name1[SIZE_PLAYERNAME-1] = '\0'; + str_name1 = pr->str_name1; + } + else if ( ! memcmp( str_line, "N-", 2 ) ) + { + strncpy( pr->str_name2, str_line+2, SIZE_PLAYERNAME-1 ); + pr->str_name2[SIZE_PLAYERNAME-1] = '\0'; + str_name2 = pr->str_name2; + } + } + if ( ! iret ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_unexpect_eof ); + str_error = str_message; + return -2; + } + + /* board representation */ + memset( &min_posi.asquare, empty, nsquare ); + min_posi.hand_black = min_posi.hand_white = 0; + is_rep1_done = is_rep2_done = is_all_done = 0; + while ( str_line[0] == 'P' ) + { + if ( str_line[1] == 'I' && ! is_rep2_done && ! is_all_done ) + { + is_rep1_done = 1; + iret = read_board_rep1( str_line, &min_posi ); + } + else if ( isdigit( (int)str_line[1] ) && str_line[1] != '0' + && ! is_rep1_done && ! is_all_done ) + { + is_rep2_done = 1; + iret = read_board_rep2( str_line, &min_posi ); + } + else if ( str_line[1] == '+' || str_line[1] == '-' ) + { + is_all_done = iret = read_board_rep3( str_line, &min_posi ); + } + else { break; } + if ( iret < 0 ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_error ); + str_error = str_message; + return iret; + } + + iret = read_CSA_line( pr, str_line ); + if ( iret < 0 ) { return iret; } + if ( ! iret ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_unexpect_eof ); + str_error = str_message; + return -2; + } + } + + /* turn to move */ + if ( strcmp( str_line, "+" ) && strcmp( str_line, "-" ) ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_bad_record ); + str_error = str_message; + return -2; + } + min_posi.turn_to_move = (char)( ( str_line[0] == '+' ) ? black : white ); + + return ini_game( ptree, &min_posi, flag, str_name1, str_name2 ); +} + + +static int +read_board_rep3( const char *str_line, min_posi_t *pmin_posi ) +{ + int is_all_done, irank, ifile, isquare, piece, n, color; + int npawn, nlance, nknight, nsilver, ngold, nbishop, nrook; + unsigned int handv, hand_white, hand_black; + char str_piece[3]; + + is_all_done = 0; + str_piece[2] = '\0'; + + color = str_line[1] == '+' ? black : white; + for ( n = 2; str_line[n] != '\0'; n += 4 ) { + if ( str_line[n+1] == '\0' || str_line[n+2] == '\0' + || str_line[n+3] == '\0' || is_all_done ) + { + str_error = str_bad_board; + return -2; + } + if ( str_line[n] == '0' && str_line[n+1] == '0' + && str_line[n+2] == 'A' && str_line[n+3] == 'L' ) { + hand_black = pmin_posi->hand_black; + hand_white = pmin_posi->hand_white; + npawn = (int)(I2HandPawn(hand_black) + I2HandPawn(hand_white)); + nlance = (int)(I2HandLance(hand_black) + I2HandLance(hand_white)); + nknight = (int)(I2HandKnight(hand_black) + I2HandKnight(hand_white)); + nsilver = (int)(I2HandSilver(hand_black) + I2HandSilver(hand_white)); + ngold = (int)(I2HandGold(hand_black) + I2HandGold(hand_white)); + nbishop = (int)(I2HandBishop(hand_black) + I2HandBishop(hand_white)); + nrook = (int)(I2HandRook(hand_black) + I2HandRook(hand_white)); + for ( isquare = 0; isquare < nsquare; isquare++ ) + switch ( abs( pmin_posi->asquare[isquare] ) ) + { + case pawn: case pro_pawn: npawn++; break; + case lance: case pro_lance: nlance++; break; + case knight: case pro_knight: nknight++; break; + case silver: case pro_silver: nsilver++; break; + case gold: ngold++; break; + case bishop: case horse: nbishop++; break; + case rook: case dragon: nrook++; break; + default: + assert( pmin_posi->asquare[isquare] == empty ); + break; + } + handv = flag_hand_pawn * ( npawn_max -npawn ); + handv += flag_hand_lance * ( nlance_max -nlance ); + handv += flag_hand_knight * ( nknight_max -nknight ); + handv += flag_hand_silver * ( nsilver_max -nsilver ); + handv += flag_hand_gold * ( ngold_max -ngold ); + handv += flag_hand_bishop * ( nbishop_max -nbishop ); + handv += flag_hand_rook * ( nrook_max -nrook ); + if ( color ) { pmin_posi->hand_white += handv; } + else { pmin_posi->hand_black += handv; } + is_all_done = 1; + continue; + } + + ifile = str_line[n+0]-'0'; + irank = str_line[n+1]-'0'; + str_piece[0] = str_line[n+2]; + str_piece[1] = str_line[n+3]; + piece = str2piece( str_piece ); + + /* hand */ + if ( ifile == 0 && ifile == 0 ) + { + switch ( piece ) + { + case pawn: handv = flag_hand_pawn; break; + case lance: handv = flag_hand_lance; break; + case knight: handv = flag_hand_knight; break; + case silver: handv = flag_hand_silver; break; + case gold: handv = flag_hand_gold; break; + case bishop: handv = flag_hand_bishop; break; + case rook: handv = flag_hand_rook; break; + default: + str_error = str_bad_board; + return -2; + } + if ( color ) { pmin_posi->hand_white += handv; } + else { pmin_posi->hand_black += handv; } + } + /* board */ + else { + ifile = 9 - ifile; + irank = irank - 1; + isquare = irank * nfile + ifile; + if ( piece == -2 || ifile < file1 || ifile > file9 + || irank < rank1 || irank > rank9 || pmin_posi->asquare[isquare] ) + { + str_error = str_bad_board; + return -2; + } + pmin_posi->asquare[isquare] = (signed char)( color ? -piece : piece ); + } + } + + return is_all_done; +} + + +static int +read_board_rep2( const char * str_line, min_posi_t *pmin_posi ) +{ + int irank, ifile, piece; + char str_piece[3]; + + str_piece[2] = '\0'; + + irank = str_line[1] - '1'; + + for ( ifile = 0; ifile < nfile; ifile++ ) + if ( str_line[2+ifile*3] == '+' || str_line[2+ifile*3] == '-' ) + { + str_piece[0] = str_line[2+ifile*3+1]; + str_piece[1] = str_line[2+ifile*3+2]; + piece = str2piece( str_piece ); + if ( piece < 0 || pmin_posi->asquare[ irank*nfile + ifile ] ) + { + str_error = str_bad_board; + return -2; + } + pmin_posi->asquare[ irank*nfile + ifile ] + = (signed char)( str_line[ 2 + ifile*3 ] == '-' ? -piece : piece ); + } + else { pmin_posi->asquare[ irank*nfile + ifile ] = empty; } + + return 1; +} + + +static int +str2piece( const char *str ) +{ + int i; + + for ( i = 0; i < 16; i++ ) + { + if ( ! strcmp( astr_table_piece[i], str ) ) { break; } + } + if ( i == 0 || i == piece_null || i == 16 ) { i = -2; } + + return i; +} + + +/* reads a csa line in str, trancates trailing spases. + * return value: + * 0 EOF, no line is read. + * 1 a csa line is read in str + * -2 buffer overflow + */ +static int +read_CSA_line( record_t *pr, char *str ) +{ + int i, c, do_comma; + + for ( ;; ) + { + c = skip_comment( pr ); + if ( isgraph( c ) || c == EOF ) { break; } + } + if ( c == EOF ) { return 0; } + + do_comma = ( c == 'N' || c == '$' ) ? 0 : 1; + + for ( i = 0; i < SIZE_CSALINE-1; i++ ) + { + if ( c == EOF || c == '\n' || ( do_comma && c == ',' ) ) { break; } + str[i] = (char)c; + c = read_char( pr ); + } + + if ( i == SIZE_CSALINE-1 ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + pr->lines, str_ovrflw_line ); + return -2; + } + + i--; + while ( isascii( (int)str[i] ) && isspace( (int)str[i] ) ) { i--; } + str[i+1] = '\0'; + + return 1; +} + + +static int +skip_comment( record_t *pr ) +{ + int c; + + c = read_char( pr ); + for ( ;; ) + { + if ( c != '\'' ) { break; } + for ( ;; ) + { + c = read_char( pr ); + if ( c == EOF || c == '\n' ) { break; } + } + } + + return c; +} + + +static int +read_char( record_t *pr ) +{ + int c; + + c = fgetc( pr->pf ); + if ( c == '\n' ) { pr->lines++; } + + return c; +} diff --git a/data.c b/data.c new file mode 100644 index 0000000..97e9500 --- /dev/null +++ b/data.c @@ -0,0 +1,327 @@ +#include +#include +#include "shogi.h" + +FILE *pf_book; +FILE *pf_hash; +uint64_t ehash_tbl[ EHASH_MASK + 1 ]; +unsigned char hash_rejections_parent[ REJEC_MASK+1 ]; +rejections_t hash_rejections[ REJEC_MASK+1 ]; +trans_table_t *ptrans_table_orig; +SHARE trans_table_t *ptrans_table; +history_book_learn_t history_book_learn[ HASH_REG_HIST_LEN ]; +record_t record_problems; +record_t record_game; +rand_work_t rand_work; +root_move_t root_move_list[ MAX_LEGAL_MOVES ]; +pv_t last_pv; +pv_t last_pv_save; +slide_tbl_t aslide[ nsquare ]; +bitboard_t abb_b_knight_attacks[ nsquare ]; +bitboard_t abb_b_silver_attacks[ nsquare ]; +bitboard_t abb_b_gold_attacks[ nsquare ]; +bitboard_t abb_w_knight_attacks[ nsquare ]; +bitboard_t abb_w_silver_attacks[ nsquare ]; +bitboard_t abb_w_gold_attacks[ nsquare ]; +bitboard_t abb_king_attacks[ nsquare ]; +bitboard_t abb_bishop_attacks_rl45[ nsquare ][ 128 ]; +bitboard_t abb_bishop_attacks_rr45[ nsquare ][ 128 ]; +bitboard_t abb_file_attacks[ nsquare ][ 128 ]; +bitboard_t abb_obstacle[ nsquare ][ nsquare ]; +bitboard_t abb_mask[ nsquare ]; +bitboard_t abb_mask_rl90[ nsquare ]; +bitboard_t abb_mask_rl45[ nsquare ]; +bitboard_t abb_mask_rr45[ nsquare ]; +bitboard_t abb_plus_rays[ nsquare ]; +bitboard_t abb_minus_rays[ nsquare ]; +uint64_t b_pawn_rand[ nsquare ]; +uint64_t b_lance_rand[ nsquare ]; +uint64_t b_knight_rand[ nsquare ]; +uint64_t b_silver_rand[ nsquare ]; +uint64_t b_gold_rand[ nsquare ]; +uint64_t b_bishop_rand[ nsquare ]; +uint64_t b_rook_rand[ nsquare ]; +uint64_t b_king_rand[ nsquare ]; +uint64_t b_pro_pawn_rand[ nsquare ]; +uint64_t b_pro_lance_rand[ nsquare ]; +uint64_t b_pro_knight_rand[ nsquare ]; +uint64_t b_pro_silver_rand[ nsquare ]; +uint64_t b_horse_rand[ nsquare ]; +uint64_t b_dragon_rand[ nsquare ]; +uint64_t b_hand_pawn_rand[ npawn_max ]; +uint64_t b_hand_lance_rand[ nlance_max ]; +uint64_t b_hand_knight_rand[ nknight_max ]; +uint64_t b_hand_silver_rand[ nsilver_max ]; +uint64_t b_hand_gold_rand[ ngold_max ]; +uint64_t b_hand_bishop_rand[ nbishop_max ]; +uint64_t b_hand_rook_rand[ nrook_max ]; +uint64_t w_pawn_rand[ nsquare ]; +uint64_t w_lance_rand[ nsquare ]; +uint64_t w_knight_rand[ nsquare ]; +uint64_t w_silver_rand[ nsquare ]; +uint64_t w_gold_rand[ nsquare ]; +uint64_t w_bishop_rand[ nsquare ]; +uint64_t w_rook_rand[ nsquare ]; +uint64_t w_king_rand[ nsquare ]; +uint64_t w_pro_pawn_rand[ nsquare ]; +uint64_t w_pro_lance_rand[ nsquare ]; +uint64_t w_pro_knight_rand[ nsquare ]; +uint64_t w_pro_silver_rand[ nsquare ]; +uint64_t w_horse_rand[ nsquare ]; +uint64_t w_dragon_rand[ nsquare ]; +uint64_t w_hand_pawn_rand[ npawn_max ]; +uint64_t w_hand_lance_rand[ nlance_max ]; +uint64_t w_hand_knight_rand[ nknight_max ]; +uint64_t w_hand_silver_rand[ nsilver_max ]; +uint64_t w_hand_gold_rand[ ngold_max ]; +uint64_t w_hand_bishop_rand[ nbishop_max ]; +uint64_t w_hand_rook_rand[ nrook_max ]; +uint64_t node_limit; +SHARE unsigned int game_status; +unsigned int move_evasion_pchk; +unsigned int node_per_second; +unsigned int node_next_signal; +unsigned int node_last_check; +unsigned int hash_mask; +unsigned int sec_elapsed; +unsigned int sec_b_total; +unsigned int sec_w_total; +unsigned int sec_limit; +unsigned int sec_limit_up; +unsigned int sec_limit_depth; +unsigned int time_last_result; +unsigned int time_last_check; +unsigned int time_start; +unsigned int time_turn_start; +unsigned int time_limit; +unsigned int time_max_limit; +unsigned int time_last_search; +unsigned int time_last_eff_search; +unsigned int time_response; +unsigned int ai_rook_attacks_r0[ nsquare ][ 128 ]; +unsigned int ponder_move; +int p_value_ex[31]; +int benefit2promo[15]; +int easy_abs; +int easy_min; +int easy_max; +int easy_value; +SHARE int fmg_misc; +SHARE int fmg_cap; +SHARE int fmg_drop; +SHARE int fmg_mt; +SHARE int fmg_misc_king; +SHARE int fmg_cap_king; +unsigned int ponder_move_list[ MAX_LEGAL_MOVES ]; +int ponder_nmove; +SHARE int root_abort; +int root_nrep; +int root_nmove; +int root_alpha; +int root_beta; +int root_value; +int root_turn; +int root_move_cap; +int root_nfail_high; +int root_nfail_low; +int trans_table_age; +int log2_ntrans_table; +int n_nobook_move; +int last_root_value; +int last_root_value_save; +int iteration_depth; +int depth_limit; +int irecord_game; +int npawn_box; +int nlance_box; +int nknight_box; +int nsilver_box; +int ngold_box; +int nbishop_box; +int nrook_box; +int resign_threshold; +short p_value[31]; +short pc_on_sq[nsquare][pos_n]; +short kkp[nsquare][nsquare][kkp_end]; +unsigned char book_section[ MAX_SIZE_SECTION+1 ]; +unsigned char adirec[ nsquare ][ nsquare ]; +unsigned char is_same[ 16 ][ 16 ]; +char str_cmdline[ SIZE_CMDLINE ]; +char str_message[ SIZE_MESSAGE ]; +char str_buffer_cmdline[ SIZE_CMDBUFFER ]; +const char *str_error; + +#if defined(MPV) +int root_mpv; +int mpv_num; +int mpv_width; +pv_t mpv_pv[ MPV_MAX_PV*2 + 1 ]; +#endif + +#if defined(TLP) +# if !defined(_WIN32) +pthread_attr_t pthread_attr; +# endif +lock_t tlp_lock; +tree_t tlp_atree_work[ TLP_NUM_WORK ]; +tree_t * volatile tlp_ptrees[ TLP_MAX_THREADS ]; +volatile int tlp_abort; +volatile int tlp_idle; +volatile int tlp_num; +int tlp_max; +int tlp_nsplit; +int tlp_nabort; +int tlp_nslot; +volatile unsigned short tlp_rejections_slot[ REJEC_MASK+1 ]; +#else +tree_t tree; +#endif + +#if ! defined(_WIN32) +clock_t clk_tck; +#endif + +#if ! defined(NO_LOGGING) +FILE *pf_log; +const char *str_dir_logs = "log"; +#endif + +#if defined(CSA_LAN) || defined(MNJ_LAN) +unsigned int time_last_send; +#endif + +#if defined(CSA_LAN) +int client_turn; +int client_ngame; +int client_max_game; +long client_port; +char client_str_addr[256]; +char client_str_id[256]; +char client_str_pwd[256]; +sckt_t sckt_csa; +#endif + +#if defined(MNJ_LAN) +short mnj_tbl[ MNJ_MASK + 1 ]; +sckt_t sckt_mnj; +int mnj_posi_id; +unsigned int mnj_move_last; +#endif + +#if defined(DEKUNOBOU) +SOCKET dek_socket_in; +SOCKET dek_s_accept; +u_long dek_ul_addr; +unsigned int dek_ngame; +unsigned int dek_lost; +unsigned int dek_win; +int dek_turn; +u_short dek_ns; +#endif + +check_table_t b_chk_tbl[nsquare]; +check_table_t w_chk_tbl[nsquare]; + +#if defined(_MSC_VER) +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) +#else +unsigned char aifirst_one[512]; +unsigned char ailast_one[512]; +#endif + +#if defined(NDEBUG) +# if ! defined(CSASHOGI) +const char *str_myname = ( "Bonanza " BNZ_VER ); +# else +const char *str_myname = ( "Bonanza " BNZ_VER ); +# endif +#else +const char *str_myname = ( "Bonanza " BNZ_VER " Debug Build (" + __TIME__ " " __DATE__ ")" ); +#endif + +#if defined(DBG_EASY) +unsigned int easy_move; +#endif + +const char *str_resign = "%TORYO"; +const char *str_repetition = "%SENNICHITE"; +const char *str_jishogi = "%JISHOGI"; +const char *str_record_error = "%ERROR"; +const char *str_delimiters = " \t,"; +const char *str_fmt_line = "Line %u: %s"; +const char *str_on = "on"; +const char *str_off = "off"; +const char *str_book = "book.bin"; +const char *str_hash = "hash.bin"; +const char *str_fv = "fv.bin"; +const char *str_book_error = "invalid opening book"; +const char *str_io_error = "I/O error"; +const char *str_perpet_check = "perpetual check"; +const char *str_bad_cmdline = "invalid command line"; +const char *str_busy_think = "I'm busy in thinking now"; +const char *str_bad_record = "invalid record of game"; +const char *str_bad_board = "invalid board representation"; +const char *str_illegal_move = "illegal move"; +const char *str_double_pawn = "double pawn"; +const char *str_mate_drppawn = "mated by a droped pawn"; +const char *str_unexpect_eof = "unexpected end of file"; +const char *str_king_hang = "The king is hang."; +const char *str_game_ended = "move after a game was concluded"; +const char *str_fopen_error = "Can't open a file"; +const char *str_ovrflw_line = "Too many characters in a line."; +const char *str_warning = "WARNING: "; +#if defined(CSA_LAN) +const char *str_server_err = "received invalid message from the server"; +#endif + +const char *astr_table_piece[16] = { "* ", "FU", "KY", "KE", "GI", "KI", + "KA", "HI", "OU", "TO", "NY", "NK", + "NG", "##", "UM", "RY" }; + +const char ach_turn[2] = { '+', '-' }; + +const char ashell_h[ SHELL_H_LEN ] = { 1, 3, 7, 15, 31, 63, 127 }; + +const short aipos[31] = { e_dragon, e_horse, 0, e_gold, + e_gold, e_gold, e_gold, 0, + e_rook, e_bishop, e_gold, e_silver, + e_knight, e_lance, e_pawn, 0, + f_pawn, f_lance, f_knight, + f_silver, f_gold, f_bishop, f_rook, + 0, f_gold, f_gold, f_gold, + f_gold, 0, f_horse, f_dragon }; + +const unsigned char aifile[ nsquare ]= { + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9, + file1, file2, file3, file4, file5, file6, file7, file8, file9 }; + +const unsigned char airank[ nsquare ]= { + rank1, rank1, rank1, rank1, rank1, rank1, rank1, rank1, rank1, + rank2, rank2, rank2, rank2, rank2, rank2, rank2, rank2, rank2, + rank3, rank3, rank3, rank3, rank3, rank3, rank3, rank3, rank3, + rank4, rank4, rank4, rank4, rank4, rank4, rank4, rank4, rank4, + rank5, rank5, rank5, rank5, rank5, rank5, rank5, rank5, rank5, + rank6, rank6, rank6, rank6, rank6, rank6, rank6, rank6, rank6, + rank7, rank7, rank7, rank7, rank7, rank7, rank7, rank7, rank7, + rank8, rank8, rank8, rank8, rank8, rank8, rank8, rank8, rank8, + rank9, rank9, rank9, rank9, rank9, rank9, rank9, rank9, rank9 }; + +const min_posi_t min_posi_no_handicap = { + 0,0,0, + { -lance, -knight, -silver, -gold, -king, -gold, -silver, -knight, -lance, + empty, -rook, empty, empty, empty, empty, empty, -bishop, empty, + -pawn, -pawn, -pawn, -pawn, -pawn, -pawn, -pawn, -pawn, -pawn, + empty, empty, empty, empty, empty, empty, empty, empty, empty, + empty, empty, empty, empty, empty, empty, empty, empty, empty, + empty, empty, empty, empty, empty, empty, empty, empty, empty, + pawn, pawn, pawn, pawn, pawn, pawn, pawn, pawn, pawn, + empty, bishop, empty, empty, empty, empty, empty, rook, empty, + lance, knight, silver, gold, king, gold, silver, knight, lance } }; diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..d18b86f --- /dev/null +++ b/debug.c @@ -0,0 +1,242 @@ +#include +#include +#include "shogi.h" + +#if !defined(NDEBUG) + +# define DOut(str) \ + out_error( "invalid %s: node= %" PRIu64 "\n", str, ptree->node_searched ); \ + return 0; + +# define CheckNum( piece ) \ + if ( n ## piece ## _max - n ## piece ## _box != n ## piece ) { \ + DOut( "number of " # piece ); \ + } + +# define CheckBoard( PIECE, piece ) \ + bb = BB_B ## PIECE; \ + while( BBToU( bb ) ) { \ + sq = FirstOne( bb ); \ + Xor( sq, bb ); \ + if ( BOARD[sq] != piece ) { DOut( "BB_B" # PIECE ); } \ + } \ + bb = BB_W ## PIECE; \ + while( BBToU( bb ) ) { \ + sq = FirstOne( bb ); \ + Xor( sq, bb ); \ + if ( BOARD[sq] != -piece ) { DOut( "BB_W" # PIECE ); } \ + } + +int +exam_bb( const tree_t *ptree ) +{ + bitboard_t bb; + uint64_t hk; + int npawn, nlance, nknight, nsilver, ngold, nbishop, nrook, npiece; + int sq, mate; + + /* leading zero-bits */ + if ( root_turn & ~0x0000001U ) { DOut( "root_turn" ); } + if ( HAND_B & ~0x01fffffU ) { DOut( "HAND_B" ); } + if ( HAND_W & ~0x01fffffU ) { DOut( "HAND_W" ); } + if ( BBToU(OCCUPIED_FILE) & ~0x7ffffffU ) { DOut( "OCCUPIED_FILE" ); } + if ( BBToU(OCCUPIED_DIAG2) & ~0x7ffffffU ) { DOut( "OCCUPIED_DIAG2" ); } + if ( BBToU(OCCUPIED_DIAG1) & ~0x7ffffffU ) { DOut( "OCCUPIED_DIAG1" ); } + + if ( BBToU(BB_BOCCUPY) & ~0x7ffffffU ) { DOut( "BB_BOCCUPY" ); } + if ( BBToU(BB_BPAWN_ATK) & ~0x7ffffffU ) { DOut( "BB_BPAWN_ATK" ); } + if ( BBToU(BB_BTGOLD) & ~0x7ffffffU ) { DOut( "BB_BTGOLD" ); } + if ( BBToU(BB_B_HDK) & ~0x7ffffffU ) { DOut( "BB_B_HDK" ); } + if ( BBToU(BB_B_BH) & ~0x7ffffffU ) { DOut( "BB_B_BH" ); } + if ( BBToU(BB_B_RD) & ~0x7ffffffU ) { DOut( "BB_B_RD" ); } + if ( BBToU(BB_BPAWN) & ~0x7ffffffU ) { DOut( "BB_BPAWN" ); } + if ( BBToU(BB_BLANCE) & ~0x7ffffffU ) { DOut( "BB_BLANCE" ); } + if ( BBToU(BB_BKNIGHT) & ~0x7ffffffU ) { DOut( "BB_BKNIGHT" ); } + if ( BBToU(BB_BSILVER) & ~0x7ffffffU ) { DOut( "BB_BSILVER" ); } + if ( BBToU(BB_BGOLD) & ~0x7ffffffU ) { DOut( "BB_BGOLD" ); } + if ( BBToU(BB_BBISHOP) & ~0x7ffffffU ) { DOut( "BB_BBISHOP" ); } + if ( BBToU(BB_BROOK) & ~0x7ffffffU ) { DOut( "BB_BROOK" ); } + if ( BBToU(BB_BPRO_PAWN) & ~0x7ffffffU ) { DOut( "BB_BPRO_PAWN" ); } + if ( BBToU(BB_BPRO_LANCE) & ~0x7ffffffU ) { DOut( "BB_BPRO_LANCE" ); } + if ( BBToU(BB_BPRO_KNIGHT) & ~0x7ffffffU ) { DOut( "BB_BPRO_KNIGHT" ); } + if ( BBToU(BB_BPRO_SILVER) & ~0x7ffffffU ) { DOut( "BB_BPRO_SILVER" ); } + if ( BBToU(BB_BHORSE) & ~0x7ffffffU ) { DOut( "BB_BHORSE" ); } + if ( BBToU(BB_BDRAGON) & ~0x7ffffffU ) { DOut( "BB_BDRAGON" ); } + + if ( BBToU(BB_WOCCUPY) & ~0x7ffffffU ) { DOut( "BB_WOCCUPY" ); } + if ( BBToU(BB_WPAWN_ATK) & ~0x7ffffffU ) { DOut( "BB_WPAWN_ATK" ); } + if ( BBToU(BB_WTGOLD) & ~0x7ffffffU ) { DOut( "BB_WTGOLD" ); } + if ( BBToU(BB_W_HDK) & ~0x7ffffffU ) { DOut( "BB_W_HDK" ); } + if ( BBToU(BB_W_BH) & ~0x7ffffffU ) { DOut( "BB_W_BH" ); } + if ( BBToU(BB_W_RD) & ~0x7ffffffU ) { DOut( "BB_W_RD" ); } + if ( BBToU(BB_WPAWN) & ~0x7ffffffU ) { DOut( "BB_WPAWN" ); } + if ( BBToU(BB_WLANCE) & ~0x7ffffffU ) { DOut( "BB_WLANCE" ); } + if ( BBToU(BB_WKNIGHT) & ~0x7ffffffU ) { DOut( "BB_WKNIGHT" ); } + if ( BBToU(BB_WSILVER) & ~0x7ffffffU ) { DOut( "BB_WSILVER" ); } + if ( BBToU(BB_WGOLD) & ~0x7ffffffU ) { DOut( "BB_WGOLD" ); } + if ( BBToU(BB_WBISHOP) & ~0x7ffffffU ) { DOut( "BB_WBISHOP" ); } + if ( BBToU(BB_WROOK) & ~0x7ffffffU ) { DOut( "BB_WROOK" ); } + if ( BBToU(BB_WPRO_PAWN) & ~0x7ffffffU ) { DOut( "BB_WPRO_PAWN" ); } + if ( BBToU(BB_WPRO_LANCE) & ~0x7ffffffU ) { DOut( "BB_WPRO_LANCE" ); } + if ( BBToU(BB_WPRO_KNIGHT) & ~0x7ffffffU ) { DOut( "BB_WPRO_KNIGHT" ); } + if ( BBToU(BB_WPRO_SILVER) & ~0x7ffffffU ) { DOut( "BB_WPRO_SILVER" ); } + if ( BBToU(BB_WHORSE) & ~0x7ffffffU ) { DOut( "BB_WHORSE" ); } + if ( BBToU(BB_WDRAGON) & ~0x7ffffffU ) { DOut( "BB_WDRAGON" ); } + + if ( BB_BPAWN.p[0] & 0x7fc0000U ) { DOut( "pawn at rank1" ); } + if ( BB_BKNIGHT.p[0] & 0x7fffe00U ) { DOut( "knight at rank1-2" ); } + + if ( BB_WPAWN.p[2] & 0x00001ffU ) { DOut( "pawn at rank9" ); } + if ( BB_WKNIGHT.p[2] & 0x003ffffU ) { DOut( "knight at rank8-9" ); } + + + /* number of pieces */ + BBOr( bb, BB_BPAWN, BB_WPAWN ); + BBOr( bb, BB_BPRO_PAWN, bb ); + BBOr( bb, BB_WPRO_PAWN, bb ); + npawn = I2HandPawn(HAND_B) + I2HandPawn(HAND_W) + PopuCount(bb); + CheckNum( pawn ); + + BBOr( bb, BB_BLANCE, BB_WLANCE ); + BBOr( bb, BB_BPRO_LANCE, bb ); + BBOr( bb, BB_WPRO_LANCE, bb ); + nlance = I2HandLance(HAND_B) + I2HandLance(HAND_W) + PopuCount(bb); + CheckNum( lance ); + + BBOr( bb, BB_BKNIGHT, BB_WKNIGHT ); + BBOr( bb, BB_BPRO_KNIGHT, bb ); + BBOr( bb, BB_WPRO_KNIGHT, bb ); + nknight = I2HandKnight(HAND_B) + I2HandKnight(HAND_W) + PopuCount(bb); + CheckNum( knight ); + + BBOr( bb, BB_BSILVER, BB_WSILVER ); + BBOr( bb, BB_BPRO_SILVER, bb ); + BBOr( bb, BB_WPRO_SILVER, bb ); + nsilver = I2HandSilver(HAND_B) + I2HandSilver(HAND_W) + PopuCount(bb); + CheckNum( silver ); + + BBOr( bb, BB_BGOLD, BB_WGOLD ); + ngold = I2HandGold(HAND_B) + I2HandGold(HAND_W) + PopuCount(bb); + CheckNum( gold ); + + BBOr( bb, BB_BBISHOP, BB_WBISHOP ); + BBOr( bb, bb, BB_BHORSE ); + BBOr( bb, bb, BB_WHORSE ); + nbishop = I2HandBishop(HAND_B) + I2HandBishop(HAND_W) + PopuCount(bb); + CheckNum( bishop ); + + BBOr( bb, BB_BROOK, BB_WROOK ); + BBOr( bb, bb, BB_BDRAGON ); + BBOr( bb, bb, BB_WDRAGON ); + nrook = I2HandRook(HAND_B) + I2HandRook(HAND_W) + PopuCount(bb); + CheckNum( rook ); + + + /* consistency of redundant bitboards */ + BBOr( bb, BB_BGOLD, BB_BPRO_PAWN ); + BBOr( bb, bb, BB_BPRO_LANCE ); + BBOr( bb, bb, BB_BPRO_KNIGHT ); + BBOr( bb, bb, BB_BPRO_SILVER ); + if ( BBCmp( bb, BB_BTGOLD ) ) { DOut( "BB_BTGOLD" ); } + + BBOr( bb, BB_BBISHOP, BB_BHORSE ); + if ( BBCmp( bb, BB_B_BH ) ) { DOut( "BB_B_BH" ); } + + BBOr( bb, BB_BROOK, BB_BDRAGON ); + if ( BBCmp( bb, BB_B_RD ) ) { DOut( "BB_B_RD" ); } + + BBOr( bb, BB_BHORSE, BB_BDRAGON ); + BBOr( bb, BB_BKING, bb ); + if ( BBCmp( bb, BB_B_HDK ) ) { DOut( "BB_B_HDK" ); } + + bb.p[0] = ( BB_BPAWN.p[0] << 9 ) & 0x7ffffffU; + bb.p[0] |= ( BB_BPAWN.p[1] >> 18 ) & 0x00001ffU; + bb.p[1] = ( BB_BPAWN.p[1] << 9 ) & 0x7ffffffU; + bb.p[1] |= ( BB_BPAWN.p[2] >> 18 ) & 0x00001ffU; + bb.p[2] = ( BB_BPAWN.p[2] << 9 ) & 0x7ffffffU; + if ( BBCmp( bb, BB_BPAWN_ATK ) ) { DOut( "BB_BPAWN_ATK" ); } + + BBOr( bb, BB_BPAWN, BB_BLANCE ); + BBOr( bb, bb, BB_BKNIGHT ); + BBOr( bb, bb, BB_BSILVER ); + BBOr( bb, bb, BB_BTGOLD ); + BBOr( bb, bb, BB_BBISHOP ); + BBOr( bb, bb, BB_BROOK ); + BBOr( bb, bb, BB_B_HDK ); + if ( BBCmp( bb, BB_BOCCUPY ) ) { DOut( "BB_BOCCUPY" ); } + + BBOr( bb, BB_WPRO_PAWN, BB_WGOLD ); + BBOr( bb, BB_WPRO_LANCE, bb ); + BBOr( bb, BB_WPRO_KNIGHT, bb ); + BBOr( bb, BB_WPRO_SILVER, bb ); + if ( BBCmp( bb, BB_WTGOLD ) ) { DOut( "BB_WTGOLD" ); } + + BBOr( bb, BB_WBISHOP, BB_WHORSE ); + if ( BBCmp( bb, BB_W_BH ) ) { DOut( "BB_W_BH" ); } + + BBOr( bb, BB_WROOK, BB_WDRAGON ); + if ( BBCmp( bb, BB_W_RD ) ) { DOut( "BB_W_RD" ); } + + BBOr( bb, BB_WHORSE, BB_WDRAGON ); + BBOr( bb, BB_WKING, bb ); + if ( BBCmp( bb, BB_W_HDK ) ) { DOut( "BB_W_HDK" ); } + + bb.p[2] = ( BB_WPAWN.p[2] >> 9 ); + bb.p[2] |= ( BB_WPAWN.p[1] << 18 ) & 0x7fc0000U; + bb.p[1] = ( BB_WPAWN.p[1] >> 9 ); + bb.p[1] |= ( BB_WPAWN.p[0] << 18 ) & 0x7fc0000U; + bb.p[0] = ( BB_WPAWN.p[0] >> 9 ); + if ( BBCmp( bb, BB_WPAWN_ATK ) ) { DOut( "BB_WPAWN_ATK" ); } + + BBOr( bb, BB_WPAWN, BB_WLANCE ); + BBOr( bb, BB_WKNIGHT, bb ); + BBOr( bb, BB_WSILVER, bb ); + BBOr( bb, BB_WTGOLD, bb ); + BBOr( bb, BB_WBISHOP, bb ); + BBOr( bb, BB_WROOK, bb ); + BBOr( bb, BB_W_HDK, bb ); + if ( BBCmp( bb, BB_WOCCUPY ) ) { DOut( "BB_WOCCUPY" ); } + + /* consistency of board-array */ + CheckBoard( PAWN, pawn ); + CheckBoard( LANCE, lance ); + CheckBoard( KNIGHT, knight ); + CheckBoard( SILVER, silver ); + CheckBoard( GOLD, gold ); + CheckBoard( BISHOP, bishop ); + CheckBoard( ROOK, rook ); + CheckBoard( KING, king ); + CheckBoard( PRO_PAWN, pro_pawn ); + CheckBoard( PRO_LANCE, pro_lance ); + CheckBoard( PRO_KNIGHT, pro_knight ); + CheckBoard( PRO_SILVER, pro_silver ); + CheckBoard( HORSE, horse ); + CheckBoard( DRAGON, dragon ); + + for ( sq = npiece = 0; sq < nsquare; sq++ ) + { + if ( BOARD[sq] ) { npiece++; } + } + if ( npiece != PopuCount( OCCUPIED_FILE ) ) { DOut( "OCCUPIED_FILE" ); } + if ( npiece != PopuCount( OCCUPIED_DIAG2 ) ) { DOut( "OCCUPIED_DIAG2" ); } + if ( npiece != PopuCount( OCCUPIED_DIAG1 ) ) { DOut( "OCCUPIED_DIAG1" ); } + + + /* Material and Hash signature */ + mate = eval_material( ptree ); + if ( mate != MATERIAL ) { DOut( "value of material" ); } + + hk = hash_func( ptree ); + if ( hk != HASH_KEY ) { DOut( "hash signature" ); } + + if ( BOARD[SQ_BKING] != king ) { DOut( "SQ_BKING" ); } + if ( BOARD[SQ_WKING] != -king ) { DOut( "SQ_WKING" ); } + + return 1; +} + +# undef DOut +# undef CheckNum +# undef CheckBoard + +#endif /* no NDEBUG */ diff --git a/dek.c b/dek.c new file mode 100644 index 0000000..b3464ac --- /dev/null +++ b/dek.c @@ -0,0 +1,314 @@ +#include +#include +#include "shogi.h" + +#if defined(DEKUNOBOU) + +int +dek_start( const char *str_addr, int port_dek, int port_bnz ) +{ + SOCKADDR_IN service; + WSADATA wsaData; + u_short dek_ns_bnz; + + /* initialize winsock */ + if ( WSAStartup( MAKEWORD(1,1), &wsaData ) ) + { + str_error = "WSAStartup() failed."; + return -2; + } + + dek_ul_addr = inet_addr( str_addr ); + if ( dek_ul_addr == INADDR_NONE ) + { + struct hostent *phe = gethostbyname( str_addr ); + if ( ! phe ) + { + str_error = str_WSAError( "gethostbyname() faild." ); + return -2; + } + dek_ul_addr = *( (u_long *)phe->h_addr_list[0] ); + } + + dek_ns = htons( (u_short)port_dek ); + dek_ns_bnz = htons( (u_short)port_bnz ); + + dek_socket_in = socket( AF_INET, SOCK_STREAM, 0 ); + if ( dek_socket_in == INVALID_SOCKET ) + { + str_error = str_WSAError( "socket() failed." ); + return -2; + } + + service.sin_family = AF_INET; + service.sin_addr.s_addr = dek_ul_addr; + service.sin_port = dek_ns_bnz; + if ( bind( dek_socket_in, (SOCKADDR *)&service, sizeof(service) ) + == SOCKET_ERROR ) + { + str_error = "bind() failed."; + return -2; + } + + if ( listen( dek_socket_in, 1 ) == SOCKET_ERROR ) + { + str_error = "listen() failed."; + return -2; + } + + dek_s_accept = (SOCKET)SOCKET_ERROR; + + return 1; +} + + +int +dek_next_game( tree_t * restrict ptree ) +{ + if ( dek_ngame != 1 && dek_turn ) + { + Out( "take a nap ..." ); + Sleep( 37000 ); + Out( " done\n" ); + } + + if ( ini_game( ptree, &min_posi_no_handicap, flag_history, NULL, NULL ) < 0 + || get_elapsed( &time_turn_start ) < 0 + || ( dek_turn && com_turn_start( ptree, 0 ) < 0 ) ) { return -1; } + + dek_turn ^= 1; + dek_ngame += 1; + + return 1; +} + + +int +dek_check( void ) +{ + struct timeval tv; + fd_set readfds; + int iret; + char ch; + + tv.tv_sec = tv.tv_usec = 0; + + if ( dek_s_accept == SOCKET_ERROR ) + { + FD_ZERO( &readfds ); +#if defined(_MSC_VER) +# pragma warning(disable:4127) +#endif + FD_SET( dek_socket_in, &readfds ); +#if defined(_MSC_VER) +# pragma warning(default:4127) +#endif + iret = select( 1, &readfds, NULL, NULL, &tv ); + if ( iret == SOCKET_ERROR ) + { + snprintf( str_message, SIZE_MESSAGE, + "select() with a socket listening failed:%d", + WSAGetLastError() ); + str_error = str_message; + return -1; + + } + if ( ! iret ) { return 0; } /* no connection is pending. */ + + dek_s_accept = accept( dek_socket_in, NULL, NULL ); + if ( dek_s_accept == SOCKET_ERROR ) + { + snprintf( str_message, SIZE_MESSAGE, + "accept() following select() failed:%d", + WSAGetLastError() ); + str_error = str_message; + return -1; + } + } + + FD_ZERO( &readfds ); +#if defined(_MSC_VER) +# pragma warning(disable:4127) +#endif + FD_SET( dek_s_accept, &readfds ); +#if defined(_MSC_VER) +# pragma warning(default:4127) +#endif + + iret = select( 0, &readfds, NULL, NULL, &tv ); + if ( iret == SOCKET_ERROR ) + { + snprintf( str_message, SIZE_MESSAGE, + "select() with a socket accepted failed:%d", + WSAGetLastError() ); + str_error = str_message; + return -1; + } + if ( ! iret ) { return 0; } /* the connection isn't closed, + nor has available data. */ + + iret = recv( dek_s_accept, &ch, 1, MSG_PEEK ); + if ( iret == SOCKET_ERROR ) + { + closesocket( dek_s_accept ); + dek_s_accept = (SOCKET)SOCKET_ERROR; + snprintf( str_message, SIZE_MESSAGE, + "recv() with flag MSG_PEEK failed:%d", + WSAGetLastError() ); + str_error = str_message; + return -1; + } + if ( ! iret ) + { + if ( closesocket( dek_s_accept ) ) + { + dek_s_accept = (SOCKET)SOCKET_ERROR; + snprintf( str_message, SIZE_MESSAGE, + "closesocket() failed:%d", WSAGetLastError() ); + str_error = str_message; + return -1; + } + dek_s_accept = (SOCKET)SOCKET_ERROR; + return 0; /* the connection has been closed. */ + } + + return 1; /* data is available for reading. */ +} + + +int +dek_in( char *str, int n ) +{ +#if defined(_MSC_VER) +# pragma warning(disable:4127) +#endif + int count_byte; + + for (;;) { + if ( dek_s_accept == SOCKET_ERROR ) + { + Out( "\nwait for new connection...\n" ); + dek_s_accept = accept( dek_socket_in, NULL, NULL ); + if ( dek_s_accept == SOCKET_ERROR ) + { + str_error = str_WSAError( "accept() failed." ); + return -1; + } + } + + count_byte = recv( dek_s_accept, str, n, 0 ); + if ( count_byte == SOCKET_ERROR ) + { + closesocket( dek_s_accept ); + dek_s_accept = (SOCKET)SOCKET_ERROR; + str_error = str_WSAError( "recv() failed." ); + return -1; + } + if ( count_byte ) { break; } + + if ( closesocket( dek_s_accept ) ) + { + dek_s_accept = (SOCKET)SOCKET_ERROR; + str_error = str_WSAError( "closesocket() failed." ); + return -1; + } + dek_s_accept = (SOCKET)SOCKET_ERROR; + } + + *( str + count_byte ) = '\0'; + Out( "recieved %s", str ); + + return count_byte; +#if defined(_MSC_VER) +# pragma warning(default:4127) +#endif +} + + +int +dek_out( const char *format, ... ) +{ + SOCKADDR_IN service; + SOCKET socket_out; + int nch, iret; + char buf[256]; + va_list arg; + + va_start( arg, format ); + nch = vsnprintf( buf, 256, format, arg ); + va_end( arg ); + + Out( "send %s", buf ); + + socket_out = socket( AF_INET, SOCK_STREAM, 0 ); + if ( socket_out == INVALID_SOCKET ) + { + snprintf( str_message, SIZE_MESSAGE, + "socket() failed:%d", WSAGetLastError() ); + str_error = str_message; + return -2; + } + + service.sin_family = AF_INET; + service.sin_addr.s_addr = dek_ul_addr; + service.sin_port = dek_ns; + if ( connect( socket_out, (SOCKADDR *)&service, sizeof(service) ) + == SOCKET_ERROR ) + { + snprintf( str_message, SIZE_MESSAGE, + "connect() failed:%d", WSAGetLastError() ); + str_error = str_message; + return -2; + } + + iret = send( socket_out, buf, nch, 0 ); + if ( iret == SOCKET_ERROR ) + { + closesocket( socket_out ); + snprintf( str_message, SIZE_MESSAGE, + "send() failed:%d", WSAGetLastError() ); + str_error = str_message; + return -2; + } + if ( iret != nch ) + { + closesocket( socket_out ); + str_error = "send() wrote partial number of bytes."; + return -2; + } + + if ( closesocket( socket_out ) ) + { + snprintf( str_message, SIZE_MESSAGE, + "closesocket() failed:%d", WSAGetLastError() ); + str_error = str_message; + return -2; + } + + return 1; +} + +int +dek_parse( char *str, int len ) +{ + if ( *str == '+' || *str == '-' ) + { + memmove( str, str+1, 6 ); + str[6] = '\0'; + } + else if ( ! strcmp( str, str_resign ) ) + { + strncpy( str, "resign", len-1 ); + str[len-1] = '\0'; + dek_win += 1; + Out( "Bonanza won against Dekunobou\n" ); + } + else { + str_error = "unknown command is recieved from Deknobou."; + return -2; + } + + return 1; +} + +# endif /* DEKUNOBOU */ diff --git a/evaldiff.c b/evaldiff.c new file mode 100644 index 0000000..c43e4fe --- /dev/null +++ b/evaldiff.c @@ -0,0 +1,216 @@ +#include +#include "shogi.h" + +void +check_futile_score_quies( const tree_t * restrict ptree, unsigned int move, + int old_val, int new_val, int turn ) +{ + const int ifrom = I2From(move); + int fsp, fmt, ipc_cap; + + if ( I2PieceMove(move) == king ) + { + fmt = new_val; + fsp = new_val - old_val; + + if ( turn ) + { + fmt += MATERIAL; + ipc_cap = -(int)UToCap(move); + } + else { + fmt -= MATERIAL; + ipc_cap = (int)UToCap(move); + } + + if ( ipc_cap ) + { + fmt -= p_value_ex[15+ipc_cap]; + fsp -= estimate_score_diff( ptree, move, turn ); + if ( fsp > fmg_cap_king ) { fmg_cap_king = fsp; } + } + else if ( fsp > fmg_misc_king ) { fmg_misc_king = fsp; } + + if ( fmt > fmg_mt ) { fmg_mt = fmt; } + } + else { + fsp = new_val - old_val - estimate_score_diff( ptree, move, turn ); + if ( turn ) + { + fmt = new_val + MATERIAL; + ipc_cap = -(int)UToCap(move); + } + else { + fmt = new_val - MATERIAL; + ipc_cap = (int)UToCap(move); + } + if ( ifrom >= nsquare ) + { + if ( fsp > fmg_drop ) { fmg_drop = fsp; } + } + else { + if ( I2IsPromote(move) ) + { + fmt -= benefit2promo[7+I2PieceMove(move)]; + } + + if ( ipc_cap ) + { + fmt -= p_value_ex[15+ipc_cap]; + if ( fsp > fmg_cap ) { fmg_cap = fsp; } + } + else if ( fsp > fmg_misc ) { fmg_misc = fsp; } + } + + if ( fmt > fmg_mt ) { fmg_mt = fmt; } + } +} + + +int +eval_max_score( const tree_t * restrict ptree, unsigned int move, + int stand_pat, int turn, int diff ) +{ + int score_mt, score_sp, ipc_cap; + + if ( I2From(move) >= nsquare ) + { + score_sp = stand_pat + diff + fmg_drop + FMG_MG; + score_mt = ( turn ? -MATERIAL : MATERIAL ) + fmg_mt + FMG_MG_MT; + } + else { + score_sp = diff + stand_pat; + score_mt = fmg_mt + FMG_MG_MT; + if ( turn ) + { + score_mt -= MATERIAL; + ipc_cap = -(int)UToCap(move); + } + else { + score_mt += MATERIAL; + ipc_cap = (int)UToCap(move); + } + if ( I2PieceMove(move) == king ) + { + if ( ipc_cap ) + { + score_mt += p_value_ex[15+ipc_cap]; + score_sp += fmg_cap_king; + } + else { score_sp += fmg_misc_king; } + score_sp += FMG_MG_KING; + } + else { + if ( ipc_cap ) + { + score_mt += p_value_ex[15+ipc_cap]; + score_sp += fmg_cap; + } + else { score_sp += fmg_misc; } + + if ( I2IsPromote(move) ) + { + score_mt += benefit2promo[7+I2PieceMove(move)]; + } + + score_sp += FMG_MG; + } + } + return score_mt < score_sp ? score_mt : score_sp; +} + + +int +estimate_score_diff( const tree_t * restrict ptree, unsigned int move, + int turn ) +{ + const int ibk = SQ_BKING; + const int iwk = Inv(SQ_WKING); + const int ifrom = I2From(move); + const int ito = I2To(move); + int diff, ipc_move, ipc_cap, ipro_pc_move; + + if ( I2PieceMove(move) == king ) + { + ipc_cap = (int)UToCap(move); + if ( ipc_cap ) + { + diff = -(int)PcOnSq( ibk, aipos[15+ipc_cap]+ito ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_cap]+Inv(ito) ); + diff /= FV_SCALE; + if ( turn ) { diff -= p_value_ex[15+ipc_cap]; } + else { diff += p_value_ex[15-ipc_cap]; } + } + else { diff = 0; } + } + else if ( ifrom >= nsquare ) + { + ipc_move = turn ? -(int)From2Drop(ifrom) : (int)From2Drop(ifrom); + diff = (int)PcOnSq( ibk, aipos[15+ipc_move]+ito ); + diff -= (int)PcOnSq( iwk, aipos[15-ipc_move]+Inv(ito) ); + diff /= FV_SCALE; + } + else { + if ( turn ) + { + ipc_move = -(int)I2PieceMove(move); + ipc_cap = (int)UToCap(move); + ipro_pc_move = ipc_move - promote; + } + else { + ipc_move = (int)I2PieceMove(move); + ipc_cap = -(int)UToCap(move); + ipro_pc_move = ipc_move + promote; + } + if ( I2IsPromote(move) && ipc_cap ) + { + diff = -(int)PcOnSq( ibk, aipos[15+ipc_move] + ifrom ); + diff += (int)PcOnSq( ibk, aipos[15+ipro_pc_move] + ito ); + diff += -(int)PcOnSq( ibk, aipos[15+ipc_cap] + ito ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ifrom) ); + diff += -(int)PcOnSq( iwk, aipos[15-ipro_pc_move] + Inv(ito) ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_cap] + Inv(ito) ); + diff /= FV_SCALE; + if ( turn ) + { + diff -= benefit2promo[7+ipc_move]; + diff -= p_value_ex[15+ipc_cap]; + } + else { + diff += benefit2promo[7+ipc_move]; + diff += p_value_ex[15+ipc_cap]; + } + } + else if ( ipc_cap ) + { + diff = -(int)PcOnSq( ibk, aipos[15+ipc_move] + ifrom ); + diff += (int)PcOnSq( ibk, aipos[15+ipc_move] + ito ); + diff += -(int)PcOnSq( ibk, aipos[15+ipc_cap] + ito ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ifrom) ); + diff += -(int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ito) ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_cap] + Inv(ito) ); + diff /= FV_SCALE; + diff += turn ? -p_value_ex[15+ipc_cap] : p_value_ex[15+ipc_cap]; + } + else if ( I2IsPromote(move) ) + { + diff = -(int)PcOnSq( ibk, aipos[15+ipc_move] + ifrom ); + diff += (int)PcOnSq( ibk, aipos[15+ipro_pc_move] + ito ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ifrom) ); + diff += -(int)PcOnSq( iwk, aipos[15-ipro_pc_move] + Inv(ito) ); + diff /= FV_SCALE; + diff += turn ? -benefit2promo[7+ipc_move] : benefit2promo[7+ipc_move]; + } + else { + diff = -(int)PcOnSq( ibk, aipos[15+ipc_move] + ifrom ); + diff += (int)PcOnSq( ibk, aipos[15+ipc_move] + ito ); + diff += (int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ifrom) ); + diff += -(int)PcOnSq( iwk, aipos[15-ipc_move] + Inv(ito) ); + diff /= FV_SCALE; + } + } + + if ( turn ) { diff = -diff; } + + return diff; +} diff --git a/evaluate.c b/evaluate.c new file mode 100644 index 0000000..85b8a63 --- /dev/null +++ b/evaluate.c @@ -0,0 +1,494 @@ +#include +#include +#include +#include "shogi.h" + +static int ehash_probe( uint64_t current_key, unsigned int hand_b, + int *pscore ); +static void ehash_store( uint64_t key, unsigned int hand_b, int score ); +static int make_list( const tree_t * restrict ptree, int * restrict pscore, + int list0[52], int list1[52] ); + +int +eval_material( const tree_t * restrict ptree ) +{ + int material, itemp; + + itemp = PopuCount( BB_BPAWN ) + (int)I2HandPawn( HAND_B ); + itemp -= PopuCount( BB_WPAWN ) + (int)I2HandPawn( HAND_W ); + material = itemp * p_value[15+pawn]; + + itemp = PopuCount( BB_BLANCE ) + (int)I2HandLance( HAND_B ); + itemp -= PopuCount( BB_WLANCE ) + (int)I2HandLance( HAND_W ); + material += itemp * p_value[15+lance]; + + itemp = PopuCount( BB_BKNIGHT ) + (int)I2HandKnight( HAND_B ); + itemp -= PopuCount( BB_WKNIGHT ) + (int)I2HandKnight( HAND_W ); + material += itemp * p_value[15+knight]; + + itemp = PopuCount( BB_BSILVER ) + (int)I2HandSilver( HAND_B ); + itemp -= PopuCount( BB_WSILVER ) + (int)I2HandSilver( HAND_W ); + material += itemp * p_value[15+silver]; + + itemp = PopuCount( BB_BGOLD ) + (int)I2HandGold( HAND_B ); + itemp -= PopuCount( BB_WGOLD ) + (int)I2HandGold( HAND_W ); + material += itemp * p_value[15+gold]; + + itemp = PopuCount( BB_BBISHOP ) + (int)I2HandBishop( HAND_B ); + itemp -= PopuCount( BB_WBISHOP ) + (int)I2HandBishop( HAND_W ); + material += itemp * p_value[15+bishop]; + + itemp = PopuCount( BB_BROOK ) + (int)I2HandRook( HAND_B ); + itemp -= PopuCount( BB_WROOK ) + (int)I2HandRook( HAND_W ); + material += itemp * p_value[15+rook]; + + itemp = PopuCount( BB_BPRO_PAWN ); + itemp -= PopuCount( BB_WPRO_PAWN ); + material += itemp * p_value[15+pro_pawn]; + + itemp = PopuCount( BB_BPRO_LANCE ); + itemp -= PopuCount( BB_WPRO_LANCE ); + material += itemp * p_value[15+pro_lance]; + + itemp = PopuCount( BB_BPRO_KNIGHT ); + itemp -= PopuCount( BB_WPRO_KNIGHT ); + material += itemp * p_value[15+pro_knight]; + + itemp = PopuCount( BB_BPRO_SILVER ); + itemp -= PopuCount( BB_WPRO_SILVER ); + material += itemp * p_value[15+pro_silver]; + + itemp = PopuCount( BB_BHORSE ); + itemp -= PopuCount( BB_WHORSE ); + material += itemp * p_value[15+horse]; + + itemp = PopuCount( BB_BDRAGON ); + itemp -= PopuCount( BB_WDRAGON ); + material += itemp * p_value[15+dragon]; + + return material; +} + + +int +evaluate( tree_t * restrict ptree, int ply, int turn ) +{ + int list0[52], list1[52]; + int nlist, score, sq_bk, sq_wk, k0, k1, l0, l1, i, j, sum; + + ptree->neval_called++; + + if ( ptree->stand_pat[ply] != score_bound ) + { + return (int)ptree->stand_pat[ply]; + } + + if ( ehash_probe( HASH_KEY, HAND_B, &score ) ) + { + score = turn ? -score : score; + ptree->stand_pat[ply] = (short)score; + + return score; + } + + + score = 0; + nlist = make_list( ptree, &score, list0, list1 ); + sq_bk = SQ_BKING; + sq_wk = Inv( SQ_WKING ); + + sum = 0; + for ( i = 0; i < nlist; i++ ) + { + k0 = list0[i]; + k1 = list1[i]; + for ( j = 0; j <= i; j++ ) + { + l0 = list0[j]; + l1 = list1[j]; + assert( k0 >= l0 && k1 >= l1 ); + sum += PcPcOnSq( sq_bk, k0, l0 ); + sum -= PcPcOnSq( sq_wk, k1, l1 ); + } + } + + score += sum; + score /= FV_SCALE; + + score += MATERIAL; + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) { score += mnj_tbl[ HASH_KEY & MNJ_MASK ]; } +#endif + +#if ! defined(MINIMUM) + if ( abs(score) > score_max_eval ) + { + out_warning( "A score at evaluate() is out of bounce." ); + } +#endif + + ehash_store( HASH_KEY, HAND_B, score ); + + score = turn ? -score : score; + ptree->stand_pat[ply] = (short)score; + + return score; + +} + + +void ehash_clear( void ) +{ + memset( ehash_tbl, 0, sizeof(ehash_tbl) ); +} + + +static int ehash_probe( uint64_t current_key, unsigned int hand_b, + int *pscore ) +{ + uint64_t hash_word, hash_key; + + hash_word = ehash_tbl[ (unsigned int)current_key & EHASH_MASK ]; + +#if ! defined(__x86_64__) + hash_word ^= hash_word << 32; +#endif + + current_key ^= (uint64_t)hand_b << 16; + current_key &= ~(uint64_t)0xffffU; + + hash_key = hash_word; + hash_key &= ~(uint64_t)0xffffU; + + if ( hash_key != current_key ) { return 0; } + + *pscore = (int)( (unsigned int)hash_word & 0xffffU ) - 32768; + + return 1; +} + + +static void ehash_store( uint64_t key, unsigned int hand_b, int score ) +{ + uint64_t hash_word; + + hash_word = key; + hash_word ^= (uint64_t)hand_b << 16; + hash_word &= ~(uint64_t)0xffffU; + hash_word |= (uint64_t)( score + 32768 ); + +#if ! defined(__x86_64__) + hash_word ^= hash_word << 32; +#endif + + ehash_tbl[ (unsigned int)key & EHASH_MASK ] = hash_word; +} + + +static int +make_list( const tree_t * restrict ptree, int * restrict pscore, + int list0[52], int list1[52] ) +{ + bitboard_t bb; + int list2[34]; + int nlist, sq, n2, i, score, sq_bk0, sq_wk0, sq_bk1, sq_wk1; + + nlist = 14; + score = 0; + sq_bk0 = SQ_BKING; + sq_wk0 = SQ_WKING; + sq_bk1 = Inv(SQ_WKING); + sq_wk1 = Inv(SQ_BKING); + + list0[ 0] = f_hand_pawn + I2HandPawn(HAND_B); + list0[ 1] = e_hand_pawn + I2HandPawn(HAND_W); + list0[ 2] = f_hand_lance + I2HandLance(HAND_B); + list0[ 3] = e_hand_lance + I2HandLance(HAND_W); + list0[ 4] = f_hand_knight + I2HandKnight(HAND_B); + list0[ 5] = e_hand_knight + I2HandKnight(HAND_W); + list0[ 6] = f_hand_silver + I2HandSilver(HAND_B); + list0[ 7] = e_hand_silver + I2HandSilver(HAND_W); + list0[ 8] = f_hand_gold + I2HandGold(HAND_B); + list0[ 9] = e_hand_gold + I2HandGold(HAND_W); + list0[10] = f_hand_bishop + I2HandBishop(HAND_B); + list0[11] = e_hand_bishop + I2HandBishop(HAND_W); + list0[12] = f_hand_rook + I2HandRook(HAND_B); + list0[13] = e_hand_rook + I2HandRook(HAND_W); + + list1[ 0] = f_hand_pawn + I2HandPawn(HAND_W); + list1[ 1] = e_hand_pawn + I2HandPawn(HAND_B); + list1[ 2] = f_hand_lance + I2HandLance(HAND_W); + list1[ 3] = e_hand_lance + I2HandLance(HAND_B); + list1[ 4] = f_hand_knight + I2HandKnight(HAND_W); + list1[ 5] = e_hand_knight + I2HandKnight(HAND_B); + list1[ 6] = f_hand_silver + I2HandSilver(HAND_W); + list1[ 7] = e_hand_silver + I2HandSilver(HAND_B); + list1[ 8] = f_hand_gold + I2HandGold(HAND_W); + list1[ 9] = e_hand_gold + I2HandGold(HAND_B); + list1[10] = f_hand_bishop + I2HandBishop(HAND_W); + list1[11] = e_hand_bishop + I2HandBishop(HAND_B); + list1[12] = f_hand_rook + I2HandRook(HAND_W); + list1[13] = e_hand_rook + I2HandRook(HAND_B); + + score += kkp[sq_bk0][sq_wk0][ kkp_hand_pawn + I2HandPawn(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_lance + I2HandLance(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_knight + I2HandKnight(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_silver + I2HandSilver(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_gold + I2HandGold(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_bishop + I2HandBishop(HAND_B) ]; + score += kkp[sq_bk0][sq_wk0][ kkp_hand_rook + I2HandRook(HAND_B) ]; + + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_pawn + I2HandPawn(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_lance + I2HandLance(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_knight + I2HandKnight(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_silver + I2HandSilver(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_gold + I2HandGold(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_bishop + I2HandBishop(HAND_W) ]; + score -= kkp[sq_bk1][sq_wk1][ kkp_hand_rook + I2HandRook(HAND_W) ]; + + n2 = 0; + bb = BB_BPAWN; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_pawn + sq; + list2[n2] = e_pawn + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_pawn + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WPAWN; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_pawn + sq; + list2[n2] = f_pawn + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_pawn + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + n2 = 0; + bb = BB_BLANCE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_lance + sq; + list2[n2] = e_lance + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_lance + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WLANCE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_lance + sq; + list2[n2] = f_lance + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_lance + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BKNIGHT; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_knight + sq; + list2[n2] = e_knight + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_knight + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WKNIGHT; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_knight + sq; + list2[n2] = f_knight + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_knight + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BSILVER; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_silver + sq; + list2[n2] = e_silver + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_silver + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WSILVER; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_silver + sq; + list2[n2] = f_silver + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_silver + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BTGOLD; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_gold + sq; + list2[n2] = e_gold + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_gold + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WTGOLD; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_gold + sq; + list2[n2] = f_gold + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_gold + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BBISHOP; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_bishop + sq; + list2[n2] = e_bishop + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_bishop + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WBISHOP; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_bishop + sq; + list2[n2] = f_bishop + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_bishop + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BHORSE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_horse + sq; + list2[n2] = e_horse + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_horse + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WHORSE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_horse + sq; + list2[n2] = f_horse + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_horse + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BROOK; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_rook + sq; + list2[n2] = e_rook + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_rook + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WROOK; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_rook + sq; + list2[n2] = f_rook + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_rook + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BDRAGON; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = f_dragon + sq; + list2[n2] = e_dragon + Inv(sq); + score += kkp[sq_bk0][sq_wk0][ kkp_dragon + sq ]; + nlist += 1; + n2 += 1; + } + + bb = BB_WDRAGON; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + list0[nlist] = e_dragon + sq; + list2[n2] = f_dragon + Inv(sq); + score -= kkp[sq_bk1][sq_wk1][ kkp_dragon + Inv(sq) ]; + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + assert( nlist <= 52 ); + *pscore += score; + return nlist; +} diff --git a/gencap.c b/gencap.c new file mode 100644 index 0000000..06ba52b --- /dev/null +++ b/gencap.c @@ -0,0 +1,460 @@ +#include "shogi.h" + +unsigned int * +b_gen_captures( const tree_t * restrict ptree, unsigned int * restrict pmove ) +{ + bitboard_t bb_movable, bb_capture, bb_piece, bb_desti; + unsigned int utemp; + int ito, ifrom; + + bb_capture = BB_WOCCUPY; + BBNot( bb_movable, BB_BOCCUPY ); + + bb_desti.p[0] = BB_BPAWN_ATK.p[0] & bb_movable.p[0]; + bb_desti.p[1] = BB_BPAWN_ATK.p[1] & bb_capture.p[1]; + bb_desti.p[2] = BB_BPAWN_ATK.p[2] & bb_capture.p[2]; + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + ifrom = ito + 9; + utemp = ( To2Move(ito) | From2Move(ifrom) | Cap2Move(-BOARD[ito]) + | Piece2Move(pawn) ); + if ( ito < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + + bb_piece = BB_BSILVER; + while ( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + BBAnd( bb_desti, bb_capture, abb_b_silver_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) | Cap2Move(-BOARD[ito]) + | Piece2Move(silver) ); + if ( ito < A6 || ifrom < A6 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_BTGOLD; + while( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + BBAnd( bb_desti, bb_capture, abb_b_gold_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) + | Piece2Move(BOARD[ifrom]) ); + } + } + + ifrom = SQ_BKING; + BBAnd( bb_desti, bb_capture, abb_king_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(king) ); + } + + bb_piece = BB_BBISHOP; + while ( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackBishop( bb_desti, ifrom ); + bb_desti.p[0] &= bb_movable.p[0]; + if ( ifrom < A6 ) + { + bb_desti.p[1] &= bb_movable.p[1]; + bb_desti.p[2] &= bb_movable.p[2]; + } + else { + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[2] &= bb_capture.p[2]; + } + + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(bishop) ); + if ( ito < A6 || ifrom < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_BROOK; + while ( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackRook( bb_desti, ifrom ); + bb_desti.p[0] &= bb_movable.p[0]; + if ( ifrom < A6 ) + { + bb_desti.p[1] &= bb_movable.p[1]; + bb_desti.p[2] &= bb_movable.p[2]; + } + else { + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[2] &= bb_capture.p[2]; + } + + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(rook) ); + if ( ito < A6 || ifrom < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_BHORSE; + while ( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackHorse( bb_desti, ifrom ); + BBAnd( bb_desti, bb_desti, bb_capture ); + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(horse) ); + } + } + + bb_piece = BB_BDRAGON; + while ( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackDragon( bb_desti, ifrom ); + BBAnd( bb_desti, bb_desti, bb_capture ); + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(dragon) ); + } + } + + bb_piece = BB_BLANCE; + while( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + bb_desti = AttackFile( ifrom ); + BBAnd( bb_desti, bb_desti, abb_minus_rays[ifrom] ); + bb_desti.p[0] &= bb_movable.p[0]; + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[2] &= bb_capture.p[2]; + + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(lance) ); + if ( ito < A7 ) { *pmove++ = utemp | FLAG_PROMO; } + else if ( ito < A6 ) + { + *pmove++ = utemp | FLAG_PROMO; + if ( UToCap(utemp) ) { *pmove++ = utemp; } + } + else { *pmove++ = utemp; } + } + } + + bb_piece = BB_BKNIGHT; + while( BBToU( bb_piece ) ) + { + ifrom = LastOne( bb_piece ); + Xor( ifrom, bb_piece ); + + bb_desti = abb_b_knight_attacks[ifrom]; + bb_desti.p[0] &= bb_movable.p[0]; + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[2] &= bb_capture.p[2]; + + while ( BBToU( bb_desti ) ) + { + ito = LastOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(-BOARD[ito]) | Piece2Move(knight) ); + if ( ito < A7 ) { *pmove++ = utemp | FLAG_PROMO; } + else if ( ito < A6 ) + { + *pmove++ = utemp | FLAG_PROMO; + if ( UToCap(utemp) ) { *pmove++ = utemp; } + } + else { *pmove++ = utemp; } + } + } + + return pmove; +} + + +unsigned int * +w_gen_captures( const tree_t * restrict ptree, unsigned int * restrict pmove ) +{ + bitboard_t bb_movable, bb_capture, bb_piece, bb_desti; + unsigned int utemp; + int ito, ifrom; + + bb_capture = BB_BOCCUPY; + BBNot( bb_movable, BB_WOCCUPY ); + + bb_desti.p[2] = BB_WPAWN_ATK.p[2] & bb_movable.p[2]; + bb_desti.p[1] = BB_WPAWN_ATK.p[1] & bb_capture.p[1]; + bb_desti.p[0] = BB_WPAWN_ATK.p[0] & bb_capture.p[0]; + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + ifrom = ito - 9; + utemp = ( To2Move(ito) | From2Move(ifrom) | Cap2Move(BOARD[ito]) + | Piece2Move(pawn) ); + if ( ito > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + + bb_piece = BB_WSILVER; + while ( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + BBAnd( bb_desti, bb_capture, abb_w_silver_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) | Cap2Move(BOARD[ito]) + | Piece2Move(silver) ); + if ( ito > I4 || ifrom > I4 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_WTGOLD; + while( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + BBAnd( bb_desti, bb_capture, abb_w_gold_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) + | Piece2Move(-BOARD[ifrom]) ); + } + } + + ifrom = SQ_WKING; + BBAnd( bb_desti, bb_capture, abb_king_attacks[ifrom] ); + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(king) ); + } + + bb_piece = BB_WBISHOP; + while ( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackBishop( bb_desti, ifrom ); + bb_desti.p[2] &= bb_movable.p[2]; + if ( ifrom > I4 ) + { + bb_desti.p[1] &= bb_movable.p[1]; + bb_desti.p[0] &= bb_movable.p[0]; + } + else { + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[0] &= bb_capture.p[0]; + } + + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(bishop) ); + if ( ito > I4 || ifrom > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_WROOK; + while ( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackRook( bb_desti, ifrom ); + bb_desti.p[2] &= bb_movable.p[2]; + if ( ifrom > I4 ) + { + bb_desti.p[1] &= bb_movable.p[1]; + bb_desti.p[0] &= bb_movable.p[0]; + } + else { + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[0] &= bb_capture.p[0]; + } + + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(rook) ); + if ( ito > I4 || ifrom > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_WHORSE; + while ( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackHorse( bb_desti, ifrom ); + BBAnd( bb_desti, bb_desti, bb_capture ); + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(horse) ); + } + } + + bb_piece = BB_WDRAGON; + while ( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + AttackDragon( bb_desti, ifrom ); + BBAnd( bb_desti, bb_desti, bb_capture ); + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + *pmove++ = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(dragon) ); + } + } + + bb_piece = BB_WLANCE; + while( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + bb_desti = AttackFile( ifrom ); + BBAnd( bb_desti, bb_desti, abb_plus_rays[ifrom] ); + bb_desti.p[2] &= bb_movable.p[2]; + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[0] &= bb_capture.p[0]; + + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(lance) ); + if ( ito > I3 ) { *pmove++ = utemp | FLAG_PROMO; } + else if ( ito > I4 ) + { + *pmove++ = utemp | FLAG_PROMO; + if ( UToCap(utemp) ) { *pmove++ = utemp; } + } + else { *pmove++ = utemp; } + } + } + + bb_piece = BB_WKNIGHT; + while( BBToU( bb_piece ) ) + { + ifrom = FirstOne( bb_piece ); + Xor( ifrom, bb_piece ); + + bb_desti = abb_w_knight_attacks[ifrom]; + bb_desti.p[2] &= bb_movable.p[2]; + bb_desti.p[1] &= bb_capture.p[1]; + bb_desti.p[0] &= bb_capture.p[0]; + + while ( BBToU( bb_desti ) ) + { + ito = FirstOne( bb_desti ); + Xor( ito, bb_desti ); + + utemp = ( To2Move(ito) | From2Move(ifrom) + | Cap2Move(BOARD[ito]) | Piece2Move(knight) ); + if ( ito > I3 ) { *pmove++ = utemp | FLAG_PROMO; } + else if ( ito > I4 ) + { + *pmove++ = utemp | FLAG_PROMO; + if ( UToCap(utemp) ) { *pmove++ = utemp; } + } + else { *pmove++ = utemp; } + } + } + + return pmove; +} diff --git a/genchk.c b/genchk.c new file mode 100644 index 0000000..dfefee0 --- /dev/null +++ b/genchk.c @@ -0,0 +1,1441 @@ +#include +#include "shogi.h" + + +static bitboard_t add_behind_attacks( int idirec, int ik, bitboard_t bb ); + + +unsigned int * +b_gen_checks( tree_t * restrict __ptree__, unsigned int * restrict pmove ) +{ + bitboard_t bb_piece, bb_rook_chk, bb_bishop_chk, bb_chk, bb_move_to; + bitboard_t bb_diag1_chk, bb_diag2_chk, bb_file_chk, bb_drop_to, bb_desti; + const tree_t * restrict ptree = __ptree__; + unsigned int u0, u1, u2; + int from, to, sq_wk, idirec; + + sq_wk = SQ_WKING; + bb_rook_chk = bb_file_chk = AttackFile( sq_wk ); + bb_rook_chk.p[aslide[sq_wk].ir0] |= AttackRank( sq_wk ); + bb_diag1_chk = AttackDiag1( sq_wk ); + bb_diag2_chk = AttackDiag2( sq_wk ); + BBOr( bb_bishop_chk, bb_diag1_chk, bb_diag2_chk ); + BBNot( bb_move_to, BB_BOCCUPY ); + BBOr( bb_drop_to, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_drop_to, bb_drop_to ); + + from = SQ_BKING; + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + BBIni( bb_chk ); + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + BBAnd( bb_chk, bb_chk, abb_king_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(king) + | Cap2Move(-BOARD[to]); + } + } + + + bb_piece = BB_BDRAGON; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBOr( bb_chk, bb_rook_chk, abb_king_attacks[sq_wk] ); + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + AttackDragon( bb_desti, from ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(dragon) + | Cap2Move(-BOARD[to]); + } + } + + bb_piece = BB_BHORSE; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBOr( bb_chk, bb_bishop_chk, abb_king_attacks[sq_wk] ); + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + AttackHorse( bb_desti, from ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(horse) + | Cap2Move(-BOARD[to]); + } + } + + u1 = BB_BROOK.p[1]; + u2 = BB_BROOK.p[2]; + while( u1 | u2 ) + { + from = last_one12( u1, u2 ); + u1 ^= abb_mask[from].p[1]; + u2 ^= abb_mask[from].p[2]; + + AttackRook( bb_desti, from ); + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + bb_chk = bb_rook_chk; + bb_chk.p[0] |= abb_king_attacks[sq_wk].p[0]; + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while ( bb_chk.p[0] ) + { + to = last_one0( bb_chk.p[0] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + + while( bb_chk.p[1] | bb_chk.p[2] ) + { + to = last_one12( bb_chk.p[1], bb_chk.p[2] ); + bb_chk.p[1] ^= abb_mask[to].p[1]; + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(-BOARD[to]); + } + } + + u0 = BB_BROOK.p[0]; + while( u0 ) + { + from = last_one0( u0 ); + u0 ^= abb_mask[from].p[0]; + + AttackRook( bb_desti, from ); + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + BBOr( bb_chk, bb_rook_chk, abb_king_attacks[sq_wk] ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + u1 = BB_BBISHOP.p[1]; + u2 = BB_BBISHOP.p[2]; + while( u1 | u2 ) + { + from = last_one12( u1, u2 ); + u1 ^= abb_mask[from].p[1]; + u2 ^= abb_mask[from].p[2]; + + AttackBishop( bb_desti, from ); + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + bb_chk = bb_bishop_chk; + bb_chk.p[0] |= abb_king_attacks[sq_wk].p[0]; + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while ( bb_chk.p[0] ) + { + to = last_one0( bb_chk.p[0] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + + while( bb_chk.p[1] | bb_chk.p[2] ) + { + to = last_one12( bb_chk.p[1], bb_chk.p[2] ); + bb_chk.p[1] ^= abb_mask[to].p[1]; + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(-BOARD[to]); + } + } + + u0 = BB_BBISHOP.p[0]; + while( u0 ) + { + from = last_one0( u0 ); + u0 ^= abb_mask[from].p[0]; + + AttackBishop( bb_desti, from ); + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + BBOr( bb_chk, bb_bishop_chk, abb_king_attacks[sq_wk] ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + + bb_piece = BB_BTGOLD; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk = abb_w_gold_attacks[sq_wk]; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_b_gold_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = ( To2Move(to) | From2Move(from) + | Piece2Move(BOARD[from]) + | Cap2Move(-BOARD[to]) ); + } + } + + + u0 = BB_BSILVER.p[0]; + while( u0 ) + { + from = last_one0( u0 ); + u0 ^= abb_mask[from].p[0]; + + bb_chk.p[0] = abb_w_gold_attacks[sq_wk].p[0]; + bb_chk.p[1] = abb_w_gold_attacks[sq_wk].p[1]; + bb_chk.p[2] = 0; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + bb_chk.p[0] &= bb_move_to.p[0] & abb_b_silver_attacks[from].p[0]; + bb_chk.p[1] &= bb_move_to.p[1] & abb_b_silver_attacks[from].p[1]; + + while( bb_chk.p[0] | bb_chk.p[1] ) + { + to = last_one01( bb_chk.p[0], bb_chk.p[1] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + bb_chk.p[1] ^= abb_mask[to].p[1]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + + u1 = BB_BSILVER.p[1] & 0x7fc0000U; + while( u1 ) + { + from = last_one1( u1 ); + u1 ^= abb_mask[from].p[1]; + + bb_chk.p[0] = abb_w_gold_attacks[sq_wk].p[0]; + bb_chk.p[1] = bb_chk.p[2] = 0; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + bb_chk.p[0] &= bb_move_to.p[0] & abb_b_silver_attacks[from].p[0]; + while ( bb_chk.p[0] ) + { + to = last_one0( bb_chk.p[0] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + + bb_piece = BB_BSILVER; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk = abb_w_silver_attacks[sq_wk]; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_b_silver_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(-BOARD[to]); + } + } + + + u0 = BB_BKNIGHT.p[0]; + u1 = BB_BKNIGHT.p[1] & 0x7fffe00U; + while( u0 | u1 ) + { + from = last_one01( u0, u1 ); + u0 ^= abb_mask[from].p[0]; + u1 ^= abb_mask[from].p[1]; + + bb_chk.p[0] = abb_w_gold_attacks[sq_wk].p[0]; + bb_chk.p[1] = bb_chk.p[2] = 0; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + bb_chk.p[0] &= abb_b_knight_attacks[from].p[0] & bb_move_to.p[0]; + + while( bb_chk.p[0] ) + { + to = last_one0( bb_chk.p[0] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + + u2 = BB_BKNIGHT.p[2]; + u1 = BB_BKNIGHT.p[1] & 0x3ffffU; + while( u2 | u1 ) + { + from = last_one12( u1, u2 ); + u2 ^= abb_mask[from].p[2]; + u1 ^= abb_mask[from].p[1]; + + bb_chk = abb_w_knight_attacks[sq_wk]; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_b_knight_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(-BOARD[to]); + } + } + + + bb_piece = BB_BLANCE; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk.p[0] = abb_w_gold_attacks[sq_wk].p[0]; + bb_chk.p[1] = bb_chk.p[2] = 0; + + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, AttackFile( from ) ); + BBAnd( bb_chk, bb_chk, abb_minus_rays[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(-BOARD[to]) | FLAG_PROMO; + } + } + + + u1 = BB_BLANCE.p[1]; + u2 = BB_BLANCE.p[2]; + while( u1| u2 ) + { + from = last_one12( u1, u2 ); + u1 ^= abb_mask[from].p[1]; + u2 ^= abb_mask[from].p[2]; + + bb_chk = bb_file_chk; + idirec = (int)adirec[sq_wk][from]; + if ( idirec && is_pinned_on_white_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_wk, bb_chk ); + BBAnd( bb_chk, bb_chk, abb_minus_rays[from] ); + } + else { BBAnd( bb_chk, bb_file_chk, abb_plus_rays[sq_wk] );} + + BBAnd( bb_chk, bb_chk, AttackFile( from ) ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + bb_chk.p[0] = bb_chk.p[0] & 0x1ffU; + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(-BOARD[to]); + } + } + + + BBAnd( bb_piece, bb_diag1_chk, BB_BPAWN ); + while ( BBToU(bb_piece) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + to = from - nfile; + if ( BOARD[to] != empty ) { continue; } + + bb_desti = AttackDiag1( from ); + if ( BBContract( bb_desti, BB_B_BH ) ) + { + *pmove = To2Move(to) | From2Move(from) + | Piece2Move(pawn) | Cap2Move(-BOARD[to]); + if ( from < A5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + } + + BBAnd( bb_piece, bb_diag2_chk, BB_BPAWN ); + while ( BBToU(bb_piece) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + to = from - nfile; + if ( BOARD[to] != empty ) { continue; } + + bb_desti = AttackDiag2( from ); + if ( BBContract( bb_desti, BB_B_BH ) ) + { + *pmove = To2Move(to) | From2Move(from) + | Piece2Move(pawn) | Cap2Move(-BOARD[to]); + if ( from < A5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + } + + BBIni( bb_chk ); + bb_chk.p[0] = abb_w_gold_attacks[sq_wk].p[0]; + if ( sq_wk < A2 ) { BBOr( bb_chk, bb_chk, abb_mask[sq_wk+nfile] ); }; + BBAnd( bb_chk, bb_chk, bb_move_to ); + BBAnd( bb_chk, bb_chk, BB_BPAWN_ATK ); + while ( BBToU(bb_chk) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + + from = to + nfile; + *pmove = To2Move(to) | From2Move(from) + | Piece2Move(pawn) | Cap2Move(-BOARD[to]); + if ( from < A5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + + + if ( IsHandGold(HAND_B) ) + { + BBAnd( bb_chk, bb_drop_to, abb_w_gold_attacks[sq_wk] ); + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | Drop2Move(gold); + } + } + + + if ( IsHandSilver(HAND_B) ) + { + BBAnd( bb_chk, bb_drop_to, abb_w_silver_attacks[sq_wk] ); + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | Drop2Move(silver); + } + } + + + if ( IsHandKnight(HAND_B) && sq_wk < A2 ) + { + to = sq_wk + 2*nfile - 1; + if ( aifile[sq_wk] != file1 && BOARD[to] == empty ) + { + *pmove++ = To2Move(to) | Drop2Move(knight); + } + + to = sq_wk + 2*nfile + 1; + if ( aifile[sq_wk] != file9 && BOARD[to] == empty ) + { + *pmove++ = To2Move(to) | Drop2Move(knight); + } + } + + + if ( IsHandPawn(HAND_B) + && sq_wk < A1 + && ! ( BBToU(BB_BPAWN) & ( mask_file1 >> aifile[sq_wk] ) ) ) + { + to = sq_wk + nfile; + if ( BOARD[to] == empty && ! is_mate_b_pawn_drop( __ptree__, to ) ) + { + *pmove++ = To2Move(to) | Drop2Move(pawn); + } + } + + + if ( IsHandLance(HAND_B) ) + { + unsigned int move; + int dist, min_dist; + + if ( (int)aifile[sq_wk] == file1 + || (int)aifile[sq_wk] == file9 ) { min_dist = 2; } + else { min_dist = 3; } + + for ( to = sq_wk+nfile, dist = 1; to < nsquare && BOARD[to] == empty; + to += nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(lance); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + if ( IsHandRook(HAND_B) ) + { + unsigned int move; + int file, dist, min_dist; + + if ( (int)aifile[sq_wk] == file1 + || (int)aifile[sq_wk] == file9 ) { min_dist = 2; } + else { min_dist = 3; } + + for ( to = sq_wk+nfile, dist = 1; to < nsquare && BOARD[to] == empty; + to += nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + for ( file = (int)aifile[sq_wk]-1, to = sq_wk-1, dist = 1; + file >= file1 && BOARD[to] == empty; + file -= 1, to -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + if ( sq_wk < A8 || I2 < sq_wk ) { min_dist = 2; } + else { min_dist = 3; } + + for ( file = (int)aifile[sq_wk]+1, to = sq_wk+1, dist = 1; + file <= file9 && BOARD[to] == empty; + file += 1, to += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + for ( to = sq_wk-nfile, dist = 1; to >= 0 && BOARD[to] == empty; + to -= nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( (int)airank[to] == rank3 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + if ( IsHandBishop(HAND_B) ) + { + unsigned int move; + int file, rank, dist; + + to = sq_wk; + file = (int)aifile[sq_wk]; + rank = (int)airank[sq_wk]; + for ( to -= 10, file -= 1, rank -= 1, dist = 1; + file >= 0 && rank >= 0 && BOARD[to] == empty; + to -= 10, file -= 1, rank -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( rank == rank3 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_wk; + file = (int)aifile[sq_wk]; + rank = (int)airank[sq_wk]; + for ( to -= 8, file += 1, rank -= 1, dist = 1; + file <= file9 && rank >= 0 && BOARD[to] == empty; + to -= 8, file += 1, rank -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( rank == rank3 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_wk; + file = (int)aifile[sq_wk]; + rank = (int)airank[sq_wk]; + for ( to += 8, file -= 1, rank += 1, dist = 1; + file >= 0 && rank <= rank9 && BOARD[to] == empty; + to += 8, file -= 1, rank += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_wk; + file = (int)aifile[sq_wk]; + rank = (int)airank[sq_wk]; + for ( to += 10, file += 1, rank += 1, dist = 1; + file <= file9 && rank <= rank9 && BOARD[to] == empty; + to += 10, file += 1, rank += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + return pmove; +} + + +unsigned int * +w_gen_checks( tree_t * restrict __ptree__, unsigned int * restrict pmove ) +{ + bitboard_t bb_piece, bb_rook_chk, bb_bishop_chk, bb_chk, bb_move_to; + bitboard_t bb_diag1_chk, bb_diag2_chk, bb_file_chk, bb_drop_to, bb_desti; + const tree_t * restrict ptree = __ptree__; + unsigned int u0, u1, u2; + int from, to, sq_bk, idirec; + + sq_bk = SQ_BKING; + bb_rook_chk = bb_file_chk = AttackFile( sq_bk ); + bb_rook_chk.p[aslide[sq_bk].ir0] |= AttackRank( sq_bk ); + bb_diag1_chk = AttackDiag1( sq_bk ); + bb_diag2_chk = AttackDiag2( sq_bk ); + AttackBishop( bb_bishop_chk, sq_bk ); + BBNot( bb_move_to, BB_WOCCUPY ); + BBOr( bb_drop_to, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_drop_to, bb_drop_to ); + + + from = SQ_WKING; + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + BBIni( bb_chk ); + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + BBAnd( bb_chk, bb_chk, abb_king_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(king) + | Cap2Move(BOARD[to]); + } + } + + + bb_piece = BB_WDRAGON; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBOr( bb_chk, bb_rook_chk, abb_king_attacks[sq_bk] ); + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + AttackDragon( bb_desti, from ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = LastOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(dragon) + | Cap2Move(BOARD[to]); + } + } + + + bb_piece = BB_WHORSE; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBOr( bb_chk, bb_bishop_chk, abb_king_attacks[sq_bk] ); + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + AttackHorse( bb_desti, from ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(horse) + | Cap2Move(BOARD[to]); + } + } + + u0 = BB_WROOK.p[0]; + u1 = BB_WROOK.p[1]; + while( u0 | u1 ) + { + from = first_one01( u0, u1 ); + u0 ^= abb_mask[from].p[0]; + u1 ^= abb_mask[from].p[1]; + + AttackRook( bb_desti, from ); + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + bb_chk = bb_rook_chk; + bb_chk.p[2] |= abb_king_attacks[sq_bk].p[2]; + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while ( bb_chk.p[2] ) + { + to = first_one2( bb_chk.p[2] ); + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + + while( bb_chk.p[0] | bb_chk.p[1] ) + { + to = first_one01( bb_chk.p[0], bb_chk.p[1] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + bb_chk.p[1] ^= abb_mask[to].p[1]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(BOARD[to]); + } + } + + u2 = BB_WROOK.p[2]; + while( u2 ) + { + from = first_one2( u2 ); + u2 ^= abb_mask[from].p[2]; + + AttackRook( bb_desti, from ); + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + BBOr( bb_chk, bb_rook_chk, abb_king_attacks[sq_bk] ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + u0 = BB_WBISHOP.p[0]; + u1 = BB_WBISHOP.p[1]; + while( u0 | u1 ) + { + from = first_one01( u0, u1 ); + u0 ^= abb_mask[from].p[0]; + u1 ^= abb_mask[from].p[1]; + + AttackBishop( bb_desti, from ); + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + bb_chk = bb_bishop_chk; + bb_chk.p[2] |= abb_king_attacks[sq_bk].p[2]; + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while ( bb_chk.p[2] ) + { + to = first_one2( bb_chk.p[2] ); + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + + while( bb_chk.p[0] | bb_chk.p[1] ) + { + to = first_one01( bb_chk.p[0], bb_chk.p[1] ); + bb_chk.p[0] ^= abb_mask[to].p[0]; + bb_chk.p[1] ^= abb_mask[to].p[1]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(BOARD[to]); + } + } + + u2 = BB_WBISHOP.p[2]; + while( u2 ) + { + from = first_one2( u2 ); + u2 ^= abb_mask[from].p[2]; + + AttackBishop( bb_desti, from ); + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + BBAnd( bb_chk, bb_desti, bb_move_to ); + } + else { + BBOr( bb_chk, bb_bishop_chk, abb_king_attacks[sq_bk] ); + BBAnd( bb_chk, bb_chk, bb_desti ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + } + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + + bb_piece = BB_WTGOLD; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk = abb_b_gold_attacks[sq_bk]; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_w_gold_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = ( To2Move(to) | From2Move(from) + | Piece2Move(-BOARD[from]) + | Cap2Move(BOARD[to]) ); + } + } + + + u2 = BB_WSILVER.p[2]; + while( u2 ) + { + from = first_one2( u2 ); + u2 ^= abb_mask[from].p[2]; + + bb_chk.p[2] = abb_b_gold_attacks[sq_bk].p[2]; + bb_chk.p[1] = abb_b_gold_attacks[sq_bk].p[1]; + bb_chk.p[0] = 0; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + bb_chk.p[2] &= bb_move_to.p[2] & abb_w_silver_attacks[from].p[2]; + bb_chk.p[1] &= bb_move_to.p[1] & abb_w_silver_attacks[from].p[1]; + + while( bb_chk.p[2] | bb_chk.p[1] ) + { + to = first_one12( bb_chk.p[1], bb_chk.p[2] ); + bb_chk.p[1] ^= abb_mask[to].p[1]; + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + + u1 = BB_WSILVER.p[1] & 0x1ffU; + while( u1 ) + { + from = first_one1( u1 ); + u1 ^= abb_mask[from].p[1]; + + bb_chk.p[2] = abb_b_gold_attacks[sq_bk].p[2]; + bb_chk.p[1] = bb_chk.p[0] = 0; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + bb_chk.p[2] &= bb_move_to.p[2] & abb_w_silver_attacks[from].p[2]; + while ( bb_chk.p[2] ) + { + to = first_one2( bb_chk.p[2] ); + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + + bb_piece = BB_WSILVER; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk = abb_b_silver_attacks[sq_bk]; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_w_silver_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(BOARD[to]); + } + } + + + u2 = BB_WKNIGHT.p[2]; + u1 = BB_WKNIGHT.p[1] & 0x3ffffU; + while( u2 | u1 ) + { + from = first_one12( u1, u2 ); + u2 ^= abb_mask[from].p[2]; + u1 ^= abb_mask[from].p[1]; + + bb_chk.p[2] = abb_b_gold_attacks[sq_bk].p[2]; + bb_chk.p[1] = bb_chk.p[0] = 0; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + bb_chk.p[2] &= abb_w_knight_attacks[from].p[2] & bb_move_to.p[2]; + + while( bb_chk.p[2] ) + { + to = first_one2( bb_chk.p[2] ); + bb_chk.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + + u0 = BB_WKNIGHT.p[0]; + u1 = BB_WKNIGHT.p[1] & 0x7fffe00U; + while( u0 | u1 ) + { + from = first_one01( u0, u1 ); + u0 ^= abb_mask[from].p[0]; + u1 ^= abb_mask[from].p[1]; + + bb_chk = abb_b_knight_attacks[sq_bk]; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, abb_w_knight_attacks[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(BOARD[to]); + } + } + + + bb_piece = BB_WLANCE; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + bb_chk.p[2] = abb_b_gold_attacks[sq_bk].p[2]; + bb_chk.p[1] = bb_chk.p[0] = 0; + + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + } + + BBAnd( bb_chk, bb_chk, AttackFile( from ) ); + BBAnd( bb_chk, bb_chk, abb_plus_rays[from] ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(BOARD[to]) | FLAG_PROMO; + } + } + + + u0 = BB_WLANCE.p[0]; + u1 = BB_WLANCE.p[1]; + while( u0 | u1 ) + { + from = first_one01( u0, u1 ); + u0 ^= abb_mask[from].p[0]; + u1 ^= abb_mask[from].p[1]; + + bb_chk = bb_file_chk; + idirec = (int)adirec[sq_bk][from]; + if ( idirec && is_pinned_on_black_king( ptree, from, idirec ) ) + { + bb_chk = add_behind_attacks( idirec, sq_bk, bb_chk ); + BBAnd( bb_chk, bb_chk, abb_plus_rays[from] ); + } + else { BBAnd( bb_chk, bb_file_chk, abb_minus_rays[sq_bk] ); } + + BBAnd( bb_chk, bb_chk, AttackFile( from ) ); + BBAnd( bb_chk, bb_chk, bb_move_to ); + bb_chk.p[2] = bb_chk.p[2] & 0x7fc0000U; + + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(BOARD[to]); + } + } + + + BBAnd( bb_piece, bb_diag1_chk, BB_WPAWN ); + while ( BBToU(bb_piece) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + to = from + nfile; + if ( BOARD[to] != empty ) { continue; } + + bb_desti = AttackDiag1( from ); + if ( BBContract( bb_desti, BB_W_BH ) ) + { + *pmove = To2Move(to) | From2Move(from) + | Piece2Move(pawn) | Cap2Move(BOARD[to]); + if ( from > I5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + } + + BBAnd( bb_piece, bb_diag2_chk, BB_WPAWN ); + while ( BBToU(bb_piece) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + to = from + nfile; + if ( BOARD[to] != empty ) { continue; } + + bb_desti = AttackDiag2( from ); + if ( BBContract( bb_desti, BB_W_BH ) ) + { + *pmove = To2Move(to) | From2Move(from) + | Piece2Move(pawn) | Cap2Move(BOARD[to]); + if ( from > I5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + } + + BBIni( bb_chk ); + bb_chk.p[2] = abb_b_gold_attacks[sq_bk].p[2]; + if ( sq_bk > I8 ) { BBOr( bb_chk, bb_chk, abb_mask[sq_bk-nfile] ); }; + BBAnd( bb_chk, bb_chk, bb_move_to ); + BBAnd( bb_chk, bb_chk, BB_WPAWN_ATK ); + while ( BBToU(bb_chk) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + + from = to - nfile; + *pmove = To2Move(to) | From2Move(from) | Piece2Move(pawn) + | Cap2Move(BOARD[to]); + if ( from > I5 ) { *pmove |= FLAG_PROMO; } + pmove += 1; + } + + + if ( IsHandGold(HAND_W) ) + { + BBAnd( bb_chk, bb_drop_to, abb_b_gold_attacks[sq_bk] ); + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | Drop2Move(gold); + } + } + + + if ( IsHandSilver(HAND_W) ) + { + BBAnd( bb_chk, bb_drop_to, abb_b_silver_attacks[sq_bk] ); + while( BBToU( bb_chk ) ) + { + to = FirstOne( bb_chk ); + Xor( to, bb_chk ); + *pmove++ = To2Move(to) | Drop2Move(silver); + } + } + + + if ( IsHandKnight(HAND_W) && sq_bk > I8 ) + { + to = sq_bk - 2*nfile - 1; + if ( aifile[sq_bk] != file1 && BOARD[to] == empty ) + { + *pmove++ = To2Move(to) | Drop2Move(knight); + } + + to = sq_bk - 2*nfile + 1; + if ( aifile[sq_bk] != file9 && BOARD[to] == empty ) + { + *pmove++ = To2Move(to) | Drop2Move(knight); + } + } + + + if ( IsHandPawn(HAND_W) + && sq_bk > I9 + && ! ( BBToU(BB_WPAWN) & ( mask_file1 >> aifile[sq_bk] ) ) ) + { + to = sq_bk - nfile; + if ( BOARD[to] == empty && ! is_mate_w_pawn_drop( __ptree__, to ) ) + { + *pmove++ = To2Move(to) | Drop2Move(pawn); + } + } + + + if ( IsHandLance(HAND_W) ) + { + unsigned int move; + int dist, min_dist; + + if ( (int)aifile[sq_bk] == file1 + || (int)aifile[sq_bk] == file9 ) { min_dist = 2; } + else { min_dist = 3; } + + for ( to = sq_bk-nfile, dist = 1; to >= 0 && BOARD[to] == empty; + to -= nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(lance); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + if ( IsHandRook(HAND_W) ) + { + unsigned int move; + int file, dist, min_dist; + + if ( (int)aifile[sq_bk] == file1 + || (int)aifile[sq_bk] == file9 ) { min_dist = 2; } + else { min_dist = 3; } + + for ( to = sq_bk-nfile, dist = 1; to >= 0 && BOARD[to] == empty; + to -= nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + for ( to = sq_bk+nfile, dist = 1; to < nsquare && BOARD[to] == empty; + to += nfile, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( (int)airank[to] == rank7 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + + if ( sq_bk < A8 || I2 < sq_bk ) { min_dist = 2; } + else { min_dist = 3; } + + for ( file = (int)aifile[sq_bk]+1, to = sq_bk+1, dist = 1; + file <= file9 && BOARD[to] == empty; + file += 1, to += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + for ( file = (int)aifile[sq_bk]-1, to = sq_bk-1, dist = 1; + file >= file1 && BOARD[to] == empty; + file -= 1, to -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(rook); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > min_dist ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + if ( IsHandBishop(HAND_W) ) + { + unsigned int move; + int file, rank, dist; + + to = sq_bk; + file = (int)aifile[sq_bk]; + rank = (int)airank[sq_bk]; + for ( to += 10, file += 1, rank += 1, dist = 1; + file <= file9 && rank <= rank9 && BOARD[to] == empty; + to += 10, file += 1, rank += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( rank == rank7 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_bk; + file = (int)aifile[sq_bk]; + rank = (int)airank[sq_bk]; + for ( to += 8, file -= 1, rank += 1, dist = 1; + file >= 0 && rank <= rank9 && BOARD[to] == empty; + to += 8, file -= 1, rank += 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( rank == rank7 ) { move |= MOVE_CHK_CLEAR; } + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_bk; + file = (int)aifile[sq_bk]; + rank = (int)airank[sq_bk]; + for ( to -= 8, file += 1, rank -= 1, dist = 1; + file <= file9 && rank >= 0 && BOARD[to] == empty; + to -= 8, file += 1, rank -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + + to = sq_bk; + file = (int)aifile[sq_bk]; + rank = (int)airank[sq_bk]; + for ( to -= 10, file -= 1, rank -= 1, dist = 1; + file >= 0 && rank >= 0 && BOARD[to] == empty; + to -= 10, file -= 1, rank -= 1, dist += 1 ) + { + move = To2Move(to) | Drop2Move(bishop); + if ( dist == 1 ) { move |= MOVE_CHK_CLEAR; } + else if ( dist > 2 ) { move |= MOVE_CHK_SET; } + *pmove++ = move; + } + } + + + return pmove; +} + + +static bitboard_t +add_behind_attacks( int idirec, int ik, bitboard_t bb ) +{ + bitboard_t bb_tmp; + + if ( idirec == direc_diag1 ) + { + bb_tmp = abb_bishop_attacks_rr45[ik][0]; + } + else if ( idirec == direc_diag2 ) + { + bb_tmp = abb_bishop_attacks_rl45[ik][0]; + } + else if ( idirec == direc_file ) + { + bb_tmp = abb_file_attacks[ik][0]; + } + else { + assert( idirec == direc_rank ); + BBIni( bb_tmp ); + bb_tmp.p[aslide[ik].ir0] = ai_rook_attacks_r0[ik][0]; + } + BBNot( bb_tmp, bb_tmp ); + BBOr( bb, bb, bb_tmp ); + + return bb; +} diff --git a/gendrop.c b/gendrop.c new file mode 100644 index 0000000..4066827 --- /dev/null +++ b/gendrop.c @@ -0,0 +1,194 @@ +#include "shogi.h" + +unsigned int * +b_gen_drop( tree_t * restrict __ptree__, unsigned int * restrict pmove ) +{ + const tree_t * restrict ptree = __ptree__; + bitboard_t bb_target; + unsigned int ihand, ibb_target0a, ibb_target0b, ibb_pawn_cmp, utemp; + unsigned int ais_pawn[nfile]; + int nhand, ito, i, nolance, noknight; + int ahand[6]; + + if ( ! HAND_B ) { return pmove; } /* return! */ + ihand = HAND_B; + nhand = 0; + if ( IsHandKnight( ihand ) ) { ahand[ nhand++ ] = Drop2Move(knight); } + noknight = nhand; + if ( IsHandLance( ihand ) ) { ahand[ nhand++ ] = Drop2Move(lance); } + nolance = nhand; + if ( IsHandSilver( ihand ) ) { ahand[ nhand++ ] = Drop2Move(silver); } + if ( IsHandGold( ihand ) ) { ahand[ nhand++ ] = Drop2Move(gold); } + if ( IsHandBishop( ihand ) ) { ahand[ nhand++ ] = Drop2Move(bishop); } + if ( IsHandRook( ihand ) ) { ahand[ nhand++ ] = Drop2Move(rook); } + + BBOr( bb_target, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_target, bb_target ); + ibb_target0a = bb_target.p[0] & 0x7fc0000U; + ibb_target0b = bb_target.p[0] & 0x003fe00U; + bb_target.p[0] &= 0x00001ffU; + bb_target.p[1] &= 0x7ffffffU; + bb_target.p[2] &= 0x7ffffffU; + + if ( IsHandPawn( ihand ) ) + { + ibb_pawn_cmp= BB_BPAWN_ATK.p[0] | BB_BPAWN_ATK.p[1] | BB_BPAWN_ATK.p[2]; + ais_pawn[0] = ibb_pawn_cmp & ( mask_file1 >> 0 ); + ais_pawn[1] = ibb_pawn_cmp & ( mask_file1 >> 1 ); + ais_pawn[2] = ibb_pawn_cmp & ( mask_file1 >> 2 ); + ais_pawn[3] = ibb_pawn_cmp & ( mask_file1 >> 3 ); + ais_pawn[4] = ibb_pawn_cmp & ( mask_file1 >> 4 ); + ais_pawn[5] = ibb_pawn_cmp & ( mask_file1 >> 5 ); + ais_pawn[6] = ibb_pawn_cmp & ( mask_file1 >> 6 ); + ais_pawn[7] = ibb_pawn_cmp & ( mask_file1 >> 7 ); + ais_pawn[8] = ibb_pawn_cmp & ( mask_file1 >> 8 ); + + while ( BBToU( bb_target ) ) + { + ito = LastOne( bb_target ); + utemp = To2Move(ito); + if ( ! ais_pawn[aifile[ito]] && ! IsMateBPawnDrop(__ptree__,ito) ) + { + *pmove++ = utemp|Drop2Move(pawn); + } + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp | ahand[i]; } + Xor( ito, bb_target ); + } + + while ( ibb_target0b ) + { + ito = last_one0( ibb_target0b ); + utemp = To2Move(ito); + if ( ! ais_pawn[aifile[ito]] && ! IsMateBPawnDrop(__ptree__,ito) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ibb_target0b ^= abb_mask[ito].p[0]; + } + } + else { + while ( BBToU( bb_target ) ) + { + ito = LastOne( bb_target ); + utemp = To2Move(ito); + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( ito, bb_target ); + } + + while ( ibb_target0b ) + { + ito = last_one0( ibb_target0b ); + utemp = To2Move(ito); + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[ i ]; } + ibb_target0b ^= abb_mask[ ito ].p[0]; + } + } + + while ( ibb_target0a ) + { + ito = last_one0( ibb_target0a ); + utemp = To2Move(ito); + for ( i = nolance; i < nhand; i++ ) { *pmove++ = utemp|ahand[ i ]; } + ibb_target0a ^= abb_mask[ ito ].p[0]; + } + + return pmove; +} + + +unsigned int * +w_gen_drop( tree_t * restrict __ptree__, unsigned int * restrict pmove ) +{ + const tree_t * restrict ptree = __ptree__; + bitboard_t bb_target; + unsigned int ihand, ibb_target2a, ibb_target2b, ibb_pawn_cmp, utemp; + unsigned int ais_pawn[nfile]; + int nhand, ito, i, nolance, noknight; + int ahand[6]; + + if ( ! HAND_W ) { return pmove; } /* return! */ + ihand = HAND_W; + nhand = 0; + if ( IsHandKnight( ihand ) ) { ahand[ nhand++ ] = Drop2Move(knight); } + noknight = nhand; + if ( IsHandLance( ihand ) ) { ahand[ nhand++ ] = Drop2Move(lance); } + nolance = nhand; + if ( IsHandSilver( ihand ) ) { ahand[ nhand++ ] = Drop2Move(silver); } + if ( IsHandGold( ihand ) ) { ahand[ nhand++ ] = Drop2Move(gold); } + if ( IsHandBishop( ihand ) ) { ahand[ nhand++ ] = Drop2Move(bishop); } + if ( IsHandRook( ihand ) ) { ahand[ nhand++ ] = Drop2Move(rook); } + + BBOr( bb_target, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_target, bb_target ); + ibb_target2a = bb_target.p[2] & 0x00001ffU; + ibb_target2b = bb_target.p[2] & 0x003fe00U; + bb_target.p[0] &= 0x7ffffffU; + bb_target.p[1] &= 0x7ffffffU; + bb_target.p[2] &= 0x7fc0000U; + + if ( IsHandPawn( ihand ) ) + { + ibb_pawn_cmp= BB_WPAWN_ATK.p[0] | BB_WPAWN_ATK.p[1] | BB_WPAWN_ATK.p[2]; + ais_pawn[0] = ibb_pawn_cmp & ( mask_file1 >> 0 ); + ais_pawn[1] = ibb_pawn_cmp & ( mask_file1 >> 1 ); + ais_pawn[2] = ibb_pawn_cmp & ( mask_file1 >> 2 ); + ais_pawn[3] = ibb_pawn_cmp & ( mask_file1 >> 3 ); + ais_pawn[4] = ibb_pawn_cmp & ( mask_file1 >> 4 ); + ais_pawn[5] = ibb_pawn_cmp & ( mask_file1 >> 5 ); + ais_pawn[6] = ibb_pawn_cmp & ( mask_file1 >> 6 ); + ais_pawn[7] = ibb_pawn_cmp & ( mask_file1 >> 7 ); + ais_pawn[8] = ibb_pawn_cmp & ( mask_file1 >> 8 ); + + while ( BBToU( bb_target ) ) + { + ito = FirstOne( bb_target ); + utemp = To2Move(ito); + if ( ! ais_pawn[aifile[ito]] && ! IsMateWPawnDrop(__ptree__,ito) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp | ahand[i]; } + Xor( ito, bb_target ); + } + + while ( ibb_target2b ) + { + ito = first_one2( ibb_target2b ); + utemp = To2Move(ito); + if ( ! ais_pawn[aifile[ito]] && ! IsMateWPawnDrop(__ptree__,ito) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp | ahand[i]; } + ibb_target2b ^= abb_mask[ito].p[2]; + } + } + else { + while ( BBToU( bb_target ) ) + { + ito = FirstOne( bb_target ); + utemp = To2Move(ito); + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( ito, bb_target ); + } + + while ( ibb_target2b ) + { + ito = first_one2( ibb_target2b ); + utemp = To2Move(ito); + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ibb_target2b ^= abb_mask[ito].p[2]; + } + } + + while ( ibb_target2a ) + { + ito = first_one2( ibb_target2a ); + utemp = To2Move(ito); + for ( i = nolance; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ibb_target2a ^= abb_mask[ito].p[2]; + } + + return pmove; +} diff --git a/genevasn.c b/genevasn.c new file mode 100644 index 0000000..f34775b --- /dev/null +++ b/genevasn.c @@ -0,0 +1,651 @@ +#include +#include +#include "shogi.h" + + +unsigned int * +b_gen_evasion( tree_t * restrict ptree, unsigned int * restrict pmove ) +{ + bitboard_t bb_desti, bb_checker, bb_inter, bb_target, bb_piece; + unsigned int hand, ubb_target0a, ubb_target0b, ubb_pawn_cmp, utemp; + unsigned ais_pawn[nfile]; + int nchecker, sq_bk, to, sq_check, idirec; + int nhand, i, nolance, noknight, from; + int ahand[6]; + + /* move the king */ + sq_bk = SQ_BKING; + + Xor( sq_bk, BB_BOCCUPY ); + XorFile( sq_bk, OCCUPIED_FILE ); + XorDiag2( sq_bk, OCCUPIED_DIAG2 ); + XorDiag1( sq_bk, OCCUPIED_DIAG1 ); + + BBNot( bb_desti, BB_BOCCUPY ); + BBAnd( bb_desti, bb_desti, abb_king_attacks[sq_bk] ); + utemp = From2Move(sq_bk) | Piece2Move(king); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + if ( ! is_black_attacked( ptree, to ) ) + { + *pmove++ = To2Move(to) | Cap2Move(-BOARD[to]) | utemp; + } + Xor( to, bb_desti ); + } + + Xor( sq_bk, BB_BOCCUPY ); + XorFile( sq_bk, OCCUPIED_FILE ); + XorDiag2( sq_bk, OCCUPIED_DIAG2 ); + XorDiag1( sq_bk, OCCUPIED_DIAG1 ); + + bb_checker = attacks_to_piece( ptree, sq_bk ); + BBAnd( bb_checker, bb_checker, BB_WOCCUPY ); + nchecker = PopuCount( bb_checker ); + if ( nchecker == 2 ) { return pmove; } + + sq_check = LastOne( bb_checker ); + bb_inter = abb_obstacle[sq_bk][sq_check]; + + /* move other pieces */ + BBOr( bb_target, bb_inter, bb_checker ); + + BBAnd( bb_desti, bb_target, BB_BPAWN_ATK ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + + from = to + 9; + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + { + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(pawn) + | Cap2Move(-BOARD[to]) ); + if ( to < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_BLANCE; + while ( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + bb_desti = AttackFile( from ); + BBAnd( bb_desti, bb_desti, abb_minus_rays[from] ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + { + to = LastOne( bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(-BOARD[to]) ); + if ( to < A6 ) { *pmove++ = utemp | FLAG_PROMO; } + if ( to >= A7 ) { *pmove++ = utemp; } + } + } + + bb_piece = BB_BKNIGHT; + while ( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_b_knight_attacks[from] ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(-BOARD[to]) ); + if ( to < A6 ) { *pmove++ = utemp | FLAG_PROMO; } + if ( to >= A7 ) { *pmove++ = utemp; } + + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_BSILVER; + while ( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_b_silver_attacks[from] ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(-BOARD[to]) ); + if ( from < A6 || to < A6 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_BTGOLD; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_b_gold_attacks[from] ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = ( To2Move(to) | From2Move(from) + | Piece2Move(BOARD[from]) + | Cap2Move(-BOARD[to]) ); + } while( BBToU( bb_desti ) ); + } + + bb_piece = BB_BBISHOP; + while ( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackBishop( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(-BOARD[to]) ); + if ( from < A6 || to < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_BROOK; + while ( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackRook( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(-BOARD[to]) ); + if ( from < A6 || to < A6 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_BHORSE; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackHorse( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti); + *pmove++ = ( To2Move(to) | From2Move(from) | Piece2Move(horse) + | Cap2Move(-BOARD[to]) ); + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_BDRAGON; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackDragon( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_bk][from]; + if ( ! idirec || ! is_pinned_on_black_king( ptree, from, idirec ) ) + do { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = ( To2Move(to) | From2Move(from) | Piece2Move(dragon) + | Cap2Move(-BOARD[to]) ); + } while ( BBToU( bb_desti ) ); + } + + if ( ! HAND_B ) { return pmove; } + if ( ! BBToU(bb_inter) ) { return pmove; } + + /* drops */ + bb_target = bb_inter; + ubb_target0a = bb_target.p[0] & 0x7fc0000U; + ubb_target0b = bb_target.p[0] & 0x003fe00U; + bb_target.p[0] &= 0x00001ffU; + bb_target.p[1] &= 0x7ffffffU; + bb_target.p[2] &= 0x7ffffffU; + + hand = HAND_B; + nhand = 0; + if ( IsHandKnight( hand ) ) { ahand[ nhand++ ] = Drop2Move(knight); } + noknight = nhand; + if ( IsHandLance( hand ) ) { ahand[ nhand++ ] = Drop2Move(lance); } + nolance = nhand; + if ( IsHandSilver( hand ) ) { ahand[ nhand++ ] = Drop2Move(silver); } + if ( IsHandGold( hand ) ) { ahand[ nhand++ ] = Drop2Move(gold); } + if ( IsHandBishop( hand ) ) { ahand[ nhand++ ] = Drop2Move(bishop); } + if ( IsHandRook( hand ) ) { ahand[ nhand++ ] = Drop2Move(rook); } + + if ( IsHandPawn( hand ) ) + { + ubb_pawn_cmp= BBToU( BB_BPAWN_ATK ); + ais_pawn[0] = ubb_pawn_cmp & ( mask_file1 >> 0 ); + ais_pawn[1] = ubb_pawn_cmp & ( mask_file1 >> 1 ); + ais_pawn[2] = ubb_pawn_cmp & ( mask_file1 >> 2 ); + ais_pawn[3] = ubb_pawn_cmp & ( mask_file1 >> 3 ); + ais_pawn[4] = ubb_pawn_cmp & ( mask_file1 >> 4 ); + ais_pawn[5] = ubb_pawn_cmp & ( mask_file1 >> 5 ); + ais_pawn[6] = ubb_pawn_cmp & ( mask_file1 >> 6 ); + ais_pawn[7] = ubb_pawn_cmp & ( mask_file1 >> 7 ); + ais_pawn[8] = ubb_pawn_cmp & ( mask_file1 >> 8 ); + + while ( BBToU( bb_target ) ) + { + to = LastOne( bb_target ); + utemp = To2Move(to); + if ( ! ais_pawn[aifile[to]] && ! IsMateBPawnDrop( ptree, to ) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( to, bb_target ); + } + + while ( ubb_target0b ) + { + to = last_one0( ubb_target0b ); + utemp = To2Move(to); + if ( ! ais_pawn[aifile[to]] && ! IsMateBPawnDrop( ptree, to ) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target0b ^= abb_mask[ to ].p[0]; + } + } + else { + while ( BBToU( bb_target ) ) + { + to = LastOne( bb_target ); + utemp = To2Move(to); + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( to, bb_target ); + } + + while ( ubb_target0b ) + { + to = last_one0( ubb_target0b ); + utemp = To2Move(to); + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target0b ^= abb_mask[ to ].p[0]; + } + } + + while ( ubb_target0a ) + { + to = last_one0( ubb_target0a ); + utemp = To2Move(to); + for ( i = nolance; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target0a ^= abb_mask[ to ].p[0]; + } + + return pmove; +} + + +unsigned int * +w_gen_evasion( tree_t * restrict ptree, unsigned int * restrict pmove ) +{ + bitboard_t bb_desti, bb_checker, bb_inter, bb_target, bb_piece; + unsigned int hand, ubb_target2a, ubb_target2b, ubb_pawn_cmp, utemp; + unsigned int ais_pawn[nfile]; + int nchecker, sq_wk, to, sq_check, idirec; + int nhand, i, nolance, noknight, from; + int ahand[6]; + + /* move the king */ + sq_wk = SQ_WKING; + + Xor( sq_wk, BB_WOCCUPY ); + XorFile( sq_wk, OCCUPIED_FILE ); + XorDiag2( sq_wk, OCCUPIED_DIAG2 ); + XorDiag1( sq_wk, OCCUPIED_DIAG1 ); + + BBNot( bb_desti, BB_WOCCUPY ); + BBAnd( bb_desti, bb_desti, abb_king_attacks[sq_wk] ); + utemp = From2Move(sq_wk) | Piece2Move(king); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + if ( ! is_white_attacked( ptree, to ) ) + { + *pmove++ = To2Move(to) | Cap2Move(BOARD[to]) | utemp; + } + Xor( to, bb_desti ); + } + + Xor( sq_wk, BB_WOCCUPY ); + XorFile( sq_wk, OCCUPIED_FILE ); + XorDiag2( sq_wk, OCCUPIED_DIAG2 ); + XorDiag1( sq_wk, OCCUPIED_DIAG1 ); + + bb_checker = attacks_to_piece( ptree, sq_wk ); + BBAnd( bb_checker, bb_checker, BB_BOCCUPY ); + nchecker = PopuCount( bb_checker ); + if ( nchecker == 2 ) { return pmove; } + + sq_check = FirstOne( bb_checker ); + bb_inter = abb_obstacle[sq_wk][sq_check]; + + /* move other pieces */ + BBOr( bb_target, bb_inter, bb_checker ); + + BBAnd( bb_desti, bb_target, BB_WPAWN_ATK ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + + from = to - 9; + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + { + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(pawn) + | Cap2Move(BOARD[to]) ); + if ( to > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_WLANCE; + while ( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + bb_desti = AttackFile( from ); + BBAnd( bb_desti, bb_desti, abb_plus_rays[from] ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + { + to = FirstOne( bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(lance) + | Cap2Move(BOARD[to]) ); + if ( to > I4 ) { *pmove++ = utemp | FLAG_PROMO; } + if ( to <= I3 ) { *pmove++ = utemp; } + } + } + + bb_piece = BB_WKNIGHT; + while ( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_w_knight_attacks[from] ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(knight) + | Cap2Move(BOARD[to]) ); + if ( to > I4 ) { *pmove++ = utemp | FLAG_PROMO; } + if ( to <= I3 ) { *pmove++ = utemp; } + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_WSILVER; + while ( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_w_silver_attacks[from] ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(silver) + | Cap2Move(BOARD[to]) ); + if ( from > I4 || to > I4 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_WTGOLD; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_target, abb_w_gold_attacks[from] ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = ( To2Move(to) | From2Move(from) + | Piece2Move(-BOARD[from]) + | Cap2Move(BOARD[to]) ); + } while( BBToU( bb_desti ) ); + } + + bb_piece = BB_WBISHOP; + while ( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackBishop( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(bishop) + | Cap2Move(BOARD[to]) ); + if ( from > I4 || to > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_WROOK; + while ( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackRook( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU( bb_desti ) ) { continue; } + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = ( To2Move(to) | From2Move(from) | Piece2Move(rook) + | Cap2Move(BOARD[to]) ); + if ( from > I4 || to > I4 ) { utemp |= FLAG_PROMO; } + *pmove++ = utemp; + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_WHORSE; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackHorse( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti); + *pmove++ = ( To2Move(to) | From2Move(from) | Piece2Move(horse) + | Cap2Move(BOARD[to]) ); + } while ( BBToU( bb_desti ) ); + } + + bb_piece = BB_WDRAGON; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackDragon( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_target ); + if ( ! BBToU(bb_desti) ) { continue; } + + idirec = (int)adirec[sq_wk][from]; + if ( ! idirec || ! is_pinned_on_white_king( ptree, from, idirec ) ) + do { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = ( To2Move(to) | From2Move(from) | Piece2Move(dragon) + | Cap2Move(BOARD[to]) ); + } while ( BBToU( bb_desti ) ); + } + + if ( ! HAND_W ) { return pmove; } + if ( ! BBToU(bb_inter) ) { return pmove; } + + /* drop */ + bb_target = bb_inter; + ubb_target2a = bb_target.p[2] & 0x00001ffU; + ubb_target2b = bb_target.p[2] & 0x003fe00U; + bb_target.p[0] &= 0x7ffffffU; + bb_target.p[1] &= 0x7ffffffU; + bb_target.p[2] &= 0x7fc0000U; + + hand = HAND_W; + nhand = 0; + if ( IsHandKnight( hand ) ) { ahand[ nhand++ ] = Drop2Move(knight); } + noknight = nhand; + if ( IsHandLance( hand ) ) { ahand[ nhand++ ] = Drop2Move(lance); } + nolance = nhand; + if ( IsHandSilver( hand ) ) { ahand[ nhand++ ] = Drop2Move(silver); } + if ( IsHandGold( hand ) ) { ahand[ nhand++ ] = Drop2Move(gold); } + if ( IsHandBishop( hand ) ) { ahand[ nhand++ ] = Drop2Move(bishop); } + if ( IsHandRook( hand ) ) { ahand[ nhand++ ] = Drop2Move(rook); } + + if ( IsHandPawn( hand ) ) + { + ubb_pawn_cmp= BBToU( BB_WPAWN_ATK ); + ais_pawn[0] = ubb_pawn_cmp & ( mask_file1 >> 0 ); + ais_pawn[1] = ubb_pawn_cmp & ( mask_file1 >> 1 ); + ais_pawn[2] = ubb_pawn_cmp & ( mask_file1 >> 2 ); + ais_pawn[3] = ubb_pawn_cmp & ( mask_file1 >> 3 ); + ais_pawn[4] = ubb_pawn_cmp & ( mask_file1 >> 4 ); + ais_pawn[5] = ubb_pawn_cmp & ( mask_file1 >> 5 ); + ais_pawn[6] = ubb_pawn_cmp & ( mask_file1 >> 6 ); + ais_pawn[7] = ubb_pawn_cmp & ( mask_file1 >> 7 ); + ais_pawn[8] = ubb_pawn_cmp & ( mask_file1 >> 8 ); + + while ( BBToU( bb_target ) ) + { + to = FirstOne( bb_target ); + utemp = To2Move(to); + if ( ! ais_pawn[aifile[to]] && ! IsMateWPawnDrop( ptree, to ) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( to, bb_target ); + } + + while ( ubb_target2b ) + { + to = first_one2( ubb_target2b ); + utemp = To2Move(to); + if ( ! ais_pawn[aifile[to]] && ! IsMateWPawnDrop( ptree, to ) ) + { + *pmove++ = utemp | Drop2Move(pawn); + } + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target2b ^= abb_mask[ to ].p[2]; + } + } + else { + while ( BBToU( bb_target ) ) + { + to = FirstOne( bb_target ); + utemp = To2Move(to); + for ( i = 0; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + Xor( to, bb_target ); + } + + while ( ubb_target2b ) + { + to = first_one2( ubb_target2b ); + utemp = To2Move(to); + for ( i = noknight; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target2b ^= abb_mask[ to ].p[2]; + } + } + + while ( ubb_target2a ) + { + to = first_one2( ubb_target2a ); + utemp = To2Move(to); + for ( i = nolance; i < nhand; i++ ) { *pmove++ = utemp|ahand[i]; } + ubb_target2a ^= abb_mask[ to ].p[2]; + } + + return pmove; +} diff --git a/gennocap.c b/gennocap.c new file mode 100644 index 0000000..1f68b4b --- /dev/null +++ b/gennocap.c @@ -0,0 +1,370 @@ +#include "shogi.h" + +unsigned int * +b_gen_nocaptures( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + bitboard_t bb_empty, bb_piece, bb_desti; + unsigned int utemp; + int to, from; + + BBOr( bb_empty, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_empty, bb_empty ); + + bb_piece.p[1] = BB_BPAWN_ATK.p[1] & bb_empty.p[1]; + bb_piece.p[2] = BB_BPAWN_ATK.p[2] & bb_empty.p[2]; + while( bb_piece.p[1] | bb_piece.p[2] ) + { + to = last_one12( bb_piece.p[1], bb_piece.p[2] ); + bb_piece.p[1] ^= abb_mask[to].p[1]; + bb_piece.p[2] ^= abb_mask[to].p[2]; + from = to + 9; + *pmove++ = To2Move( to ) | From2Move( from ) | Piece2Move( pawn ); + } + + bb_piece = BB_BSILVER; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_b_silver_attacks[from] ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = To2Move( to ) | From2Move( from ) | Piece2Move( silver ); + if ( from < A6 || to < A6 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_BTGOLD; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_b_gold_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( BOARD[from] ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + from = SQ_BKING; + BBAnd( bb_desti, bb_empty, abb_king_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( king ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + + bb_piece.p[1] = BB_BBISHOP.p[1]; + bb_piece.p[2] = BB_BBISHOP.p[2]; + while( bb_piece.p[1] | bb_piece.p[2] ) + { + from = last_one12( bb_piece.p[1], bb_piece.p[2] ); + bb_piece.p[1] ^= abb_mask[from].p[1]; + bb_piece.p[2] ^= abb_mask[from].p[2]; + + AttackBishop( bb_desti, from ); + bb_desti.p[1] &= bb_empty.p[1]; + bb_desti.p[2] &= bb_empty.p[2]; + + utemp = From2Move( from ) | Piece2Move( bishop ); + while ( bb_desti.p[1] | bb_desti.p[2] ) + { + to = last_one12( bb_desti.p[1], bb_desti.p[2] ); + bb_desti.p[1] ^= abb_mask[to].p[1]; + bb_desti.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece.p[1] = BB_BROOK.p[1]; + bb_piece.p[2] = BB_BROOK.p[2]; + while( bb_piece.p[1] | bb_piece.p[2] ) + { + from = last_one12( bb_piece.p[1], bb_piece.p[2] ); + bb_piece.p[1] ^= abb_mask[from].p[1]; + bb_piece.p[2] ^= abb_mask[from].p[2]; + + AttackRook( bb_desti, from ); + bb_desti.p[1] &= bb_empty.p[1]; + bb_desti.p[2] &= bb_empty.p[2]; + + utemp = From2Move( from ) | Piece2Move( rook ); + while ( bb_desti.p[1] | bb_desti.p[2] ) + { + to = last_one12( bb_desti.p[1], bb_desti.p[2] ); + bb_desti.p[1] ^= abb_mask[to].p[1]; + bb_desti.p[2] ^= abb_mask[to].p[2]; + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_BHORSE; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackHorse( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_empty ); + utemp = From2Move( from ) | Piece2Move( horse ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_BDRAGON; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + AttackDragon( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_empty ); + utemp = From2Move( from ) | Piece2Move( dragon ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_empty.p[0] &= 0x1ffU; + + bb_piece = BB_BLANCE; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + bb_desti = AttackFile( from ); + BBAnd( bb_desti, bb_desti, abb_minus_rays[from] ); + BBAnd( bb_desti, bb_desti, bb_empty ); + + utemp = From2Move( from ) | Piece2Move( lance ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_BKNIGHT; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_b_knight_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( knight ); + while ( BBToU( bb_desti ) ) + { + to = LastOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + return pmove; +} + + +unsigned int * +w_gen_nocaptures( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + bitboard_t bb_empty, bb_piece, bb_desti; + unsigned int utemp; + int to, from; + + BBOr( bb_empty, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_empty, bb_empty ); + + bb_piece.p[0] = BB_WPAWN_ATK.p[0] & bb_empty.p[0]; + bb_piece.p[1] = BB_WPAWN_ATK.p[1] & bb_empty.p[1]; + while( bb_piece.p[0] | bb_piece.p[1] ) + { + to = first_one01( bb_piece.p[0], bb_piece.p[1] ); + bb_piece.p[0] ^= abb_mask[to].p[0]; + bb_piece.p[1] ^= abb_mask[to].p[1]; + from = to - 9; + *pmove++ = To2Move( to ) | From2Move( from ) | Piece2Move( pawn ); + } + + bb_piece = BB_WSILVER; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_w_silver_attacks[from] ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + + utemp = To2Move( to ) | From2Move( from ) | Piece2Move( silver ); + if ( from > I4 || to > I4 ) { *pmove++ = utemp | FLAG_PROMO; } + *pmove++ = utemp; + } + } + + bb_piece = BB_WTGOLD; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_w_gold_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( -BOARD[from] ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + from = SQ_WKING; + BBAnd( bb_desti, bb_empty, abb_king_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( king ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + + bb_piece.p[0] = BB_WBISHOP.p[0]; + bb_piece.p[1] = BB_WBISHOP.p[1]; + while( bb_piece.p[0] | bb_piece.p[1] ) + { + from = first_one01( bb_piece.p[0], bb_piece.p[1] ); + bb_piece.p[0] ^= abb_mask[from].p[0]; + bb_piece.p[1] ^= abb_mask[from].p[1]; + + AttackBishop( bb_desti, from ); + bb_desti.p[0] &= bb_empty.p[0]; + bb_desti.p[1] &= bb_empty.p[1]; + + utemp = From2Move( from ) | Piece2Move( bishop ); + while ( bb_desti.p[0] | bb_desti.p[1] ) + { + to = first_one01( bb_desti.p[0], bb_desti.p[1] ); + bb_desti.p[0] ^= abb_mask[to].p[0]; + bb_desti.p[1] ^= abb_mask[to].p[1]; + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece.p[0] = BB_WROOK.p[0]; + bb_piece.p[1] = BB_WROOK.p[1]; + while( bb_piece.p[0] | bb_piece.p[1] ) + { + from = first_one01( bb_piece.p[0], bb_piece.p[1] ); + bb_piece.p[0] ^= abb_mask[from].p[0]; + bb_piece.p[1] ^= abb_mask[from].p[1]; + + AttackRook( bb_desti, from ); + bb_desti.p[0] &= bb_empty.p[0]; + bb_desti.p[1] &= bb_empty.p[1]; + + utemp = From2Move( from ) | Piece2Move( rook ); + while ( bb_desti.p[0] | bb_desti.p[1] ) + { + to = first_one01( bb_desti.p[0], bb_desti.p[1] ); + bb_desti.p[0] ^= abb_mask[to].p[0]; + bb_desti.p[1] ^= abb_mask[to].p[1]; + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_WHORSE; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackHorse( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_empty ); + utemp = From2Move( from ) | Piece2Move( horse ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_WDRAGON; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + AttackDragon( bb_desti, from ); + BBAnd( bb_desti, bb_desti, bb_empty ); + utemp = From2Move( from ) | Piece2Move( dragon ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_empty.p[2] &= 0x7fc0000U; + + bb_piece = BB_WLANCE; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + bb_desti = AttackFile( from ); + BBAnd( bb_desti, bb_desti, abb_plus_rays[from] ); + BBAnd( bb_desti, bb_desti, bb_empty ); + + utemp = From2Move( from ) | Piece2Move( lance ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + bb_piece = BB_WKNIGHT; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + Xor( from, bb_piece ); + + BBAnd( bb_desti, bb_empty, abb_w_knight_attacks[from] ); + utemp = From2Move( from ) | Piece2Move( knight ); + while ( BBToU( bb_desti ) ) + { + to = FirstOne( bb_desti ); + Xor( to, bb_desti ); + *pmove++ = To2Move( to ) | utemp; + } + } + + return pmove; +} diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..5ab568a --- /dev/null +++ b/hash.c @@ -0,0 +1,1107 @@ +#include +#include +#include +#include +#include "shogi.h" + +static int eval_supe( unsigned int hand_current, unsigned int hand_hash, + int turn_current, int turn_hash, + int * restrict pvalue_hash, int * restrict ptype_hash ); + +int +ini_trans_table( void ) +{ + size_t size; + unsigned int ntrans_table; + + ntrans_table = 1U << log2_ntrans_table; + size = sizeof( trans_table_t ) * ntrans_table + 15U; + ptrans_table_orig = memory_alloc( size ); + if ( ptrans_table_orig == NULL ) { return -1; } + ptrans_table = (trans_table_t *)( ((ptrdiff_t)ptrans_table_orig+15) + & ~(ptrdiff_t)15U ); + hash_mask = ntrans_table - 1; + Out( "Trans. Table Entries = %dK (%dMB)\n", + ( ntrans_table * 3U ) / 1024U, size / (1024U * 1024U ) ); + + return clear_trans_table(); +} + + +#define Foo( PIECE, piece ) bb = BB_B ## PIECE; \ + while( BBToU(bb) ) { \ + sq = FirstOne( bb ); \ + Xor( sq, bb ); \ + key ^= ( b_ ## piece ## _rand )[sq]; \ + } \ + bb = BB_W ## PIECE; \ + while( BBToU(bb) ) { \ + sq = FirstOne( bb ); \ + Xor( sq, bb ); \ + key ^= ( w_ ## piece ## _rand )[sq]; \ + } + +uint64_t +hash_func( const tree_t * restrict ptree ) +{ + uint64_t key = 0; + bitboard_t bb; + int sq; + + key ^= b_king_rand[SQ_BKING]; + key ^= w_king_rand[SQ_WKING]; + + Foo( PAWN, pawn ); + Foo( LANCE, lance ); + Foo( KNIGHT, knight ); + Foo( SILVER, silver ); + Foo( GOLD, gold ); + Foo( BISHOP, bishop ); + Foo( ROOK, rook ); + Foo( PRO_PAWN, pro_pawn ); + Foo( PRO_LANCE, pro_lance ); + Foo( PRO_KNIGHT, pro_knight ); + Foo( PRO_SILVER, pro_silver ); + Foo( HORSE, horse ); + Foo( DRAGON, dragon ); + + return key; +} + +#undef Foo + + +/* + name bits shifts +word1 depth 8 56 + value 16 40 + move 19 21 + hand 21 0 +word2 key 57 7 + turn 1 6 + threat 1 5 + type 2 3 + age 3 0 + */ + +void +hash_store( const tree_t * restrict ptree, int ply, int depth, int turn, + int value_type, int value, unsigned int move, + unsigned int state_node ) +{ + uint64_t word1, word2, hash_word1, hash_word2; + unsigned int index, slot; + int depth_hash, age_hash; + +#if ! defined(MINIMUM) + if ( game_status & flag_learning ) { return; } +#endif + assert( depth <= 0xff ); + + if ( depth < 0 ) { depth = 0; } + if ( abs(value) > score_max_eval ) + { + if ( abs(value) > score_mate1ply ) { return; } + if ( value > 0 ) { value += ply-1; } + else { value -= ply-1; } +#if ! defined(MINIMUM) + if ( abs(value) > score_mate1ply ) + { + out_warning( "A stored hash value is out of bounce!" ); + } +#endif + } + word2 = ( ( HASH_KEY & ~(uint64_t)0x7fU ) + | (uint64_t)( (turn<<6) | ( state_node & node_mate_threat ) + | (value_type<<3) | trans_table_age ) ); + word1 = ( ( (uint64_t)( depth<<16 | (value+32768) ) << 40 ) + | ( (uint64_t)( move & 0x7ffffU ) << 21 ) + | HAND_B ); + + index = (unsigned int)HASH_KEY & hash_mask; + hash_word1 = ptrans_table[index].prefer.word1; + hash_word2 = ptrans_table[index].prefer.word2; + SignKey( hash_word2, hash_word1 ); + age_hash = (int)((unsigned int)(hash_word2 ) & 0x07U); + depth_hash = (int)((unsigned int)(hash_word1>>56) & 0xffU); + + SignKey( word2, word1 ); + + if ( age_hash != trans_table_age || depth_hash <= depth ) + { + ptrans_table[index].prefer.word1 = word1; + ptrans_table[index].prefer.word2 = word2; + } + else { + slot = (unsigned int)HASH_KEY >> 31; + ptrans_table[index].always[slot].word1 = word1; + ptrans_table[index].always[slot].word2 = word2; + } +} + + +void +hash_store_pv( const tree_t * restrict ptree, unsigned int move, int turn ) +{ + uint64_t key_turn_pv, word1, word2; + unsigned int index; + + key_turn_pv = ( HASH_KEY & ~(uint64_t)0x7fU ) | (unsigned int)( turn << 6 ); + index = (unsigned int)HASH_KEY & hash_mask; + + word1 = ptrans_table[index].prefer.word1; + word2 = ptrans_table[index].prefer.word2; + SignKey( word2, word1 ); + + if ( ( (unsigned int)word1 & 0x1fffffU ) == HAND_B + && ( word2 & ~(uint64_t)0x3fU ) == key_turn_pv ) + { + if ( ( (unsigned int)(word1>>21) & 0x7ffffU ) != ( move & 0x7ffffU ) ) + { + word1 &= ~((uint64_t)0x7ffffU << 21); + word1 |= (uint64_t)( move & 0x7ffffU ) << 21; + word2 &= ~((uint64_t)0x3U << 3); + SignKey( word2, word1 ); + ptrans_table[index].prefer.word1 = word1; + ptrans_table[index].prefer.word2 = word2; + } + } + else { + unsigned int slot; + + slot = (unsigned int)HASH_KEY >> 31; + word1 = ptrans_table[index].always[slot].word1; + word2 = ptrans_table[index].always[slot].word2; + SignKey( word2, word1 ); + if ( ( (unsigned int)word1 & 0x1fffffU ) == HAND_B + && ( word2 & ~(uint64_t)0x3fU ) == key_turn_pv ) + { + if ( ( (unsigned int)(word1>>21) & 0x7ffffU ) + != ( move & 0x7ffffU ) ) + { + word1 &= ~((uint64_t)0x7ffffU << 21); + word1 |= (uint64_t)( move & 0x7ffffU ) << 21; + word2 &= ~((uint64_t)0x3U << 3); + SignKey( word2, word1 ); + ptrans_table[index].always[slot].word1 = word1; + ptrans_table[index].always[slot].word2 = word2; + } + } + else { + word1 = (uint64_t)32768U << 40; + word1 |= (uint64_t)( move & 0x7ffffU ) << 21; + word1 |= HAND_B; + word2 = key_turn_pv | trans_table_age; + SignKey( word2, word1 ); + ptrans_table[index].prefer.word1 = word1; + ptrans_table[index].prefer.word2 = word2; + } + } +} + + +trans_entry_t +hash_learn_store( const tree_t * restrict ptree, int depth, int value, + unsigned int move ) +{ + trans_entry_t ret; + + assert( 0 <= depth && depth <= 0xff ); + + ret.word2 = ( (HASH_KEY&(~(uint64_t)0x7fU)) + | (uint64_t)( (root_turn<<6) + | (value_exact<<3) | trans_table_age ) ); + ret.word1 = ( ( (uint64_t)( depth<<16 | (value+32768) ) << 40 ) + | ( (uint64_t)( move & 0x7ffffU ) << 21 ) + | HAND_B ); + + return ret; +} + + +int +all_hash_learn_store( void ) +{ + uint64_t aword[2]; + unsigned int u32key, unext, u, index; + + if ( pf_hash == NULL ) { return 0; } + + if ( fseek( pf_hash, sizeof(unsigned int), SEEK_SET ) == EOF + || fread( &unext, sizeof(unsigned int), 1, pf_hash ) != 1 ) + { + str_error = str_io_error; + return -2; + } + if ( ++unext == 0x10000U ) { unext = 0x1U; } + if ( fseek( pf_hash, (long)( 20U * unext ), SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + for ( u = 0;; u++ ) + { + if ( fread( &u32key, sizeof(unsigned int), 1, pf_hash ) != 1 + || fread( aword, sizeof(uint64_t), 2, pf_hash ) != 2 ) + { + str_error = str_io_error; + return -2; + } + index = u32key & hash_mask; + aword[1] |= (uint64_t)trans_table_age; + SignKey( aword[1], aword[0] ); + ptrans_table[index].prefer.word1 = aword[0]; + ptrans_table[index].prefer.word2 = aword[1]; + if ( u == 0xfffeU ) { break; } + if ( ++unext == 0x10000U ) + { + unext = 0x1U; + if ( fseek( pf_hash, 20, SEEK_SET ) == EOF ) + { + str_error = str_io_error; + return -2; + } + } + } + + return 1; +} + + +unsigned int +hash_probe( tree_t * restrict ptree, int ply, int depth_current, + int turn_current, int alpha, int beta, unsigned int state_node ) +{ + uint64_t word1, word2, key_current, key_hash; + unsigned int hand_hash, move_hash, move_infe, move_supe, slot, utemp; + unsigned int state_node_hash, index; + int null_depth, value_hash, ifrom; + int turn_hash, depth_hash, type_hash, is_superior; + + ptree->ntrans_probe++; + move_supe = 0; + move_infe = 0; + + if ( depth_current < 0 ) { depth_current = 0; } + null_depth = NullDepth( depth_current ); + if ( null_depth < PLY_INC ) { null_depth = 0; } + + key_current = HASH_KEY & ~(uint64_t)0x7fU; + + index = (unsigned int)HASH_KEY & hash_mask; + word1 = ptrans_table[index].prefer.word1; + word2 = ptrans_table[index].prefer.word2; + SignKey( word2, word1 ); + key_hash = word2 & ~(uint64_t)0x7fU; + + if ( key_hash == key_current ) { + + ptree->ntrans_prefer_hit++; + + depth_hash = (int)((unsigned int)(word1>>56) & 0x00ffU); + value_hash = (int)((unsigned int)(word1>>40) & 0xffffU) - 32768; + move_hash = (unsigned int)(word1>>21) & 0x7ffffU; + hand_hash = (unsigned int)word1 & 0x1fffffU; + + utemp = (unsigned int)word2; + state_node_hash = utemp & node_mate_threat; + turn_hash = (int)((utemp>>6) & 0x1U); + type_hash = (int)((utemp>>3) & 0x3U); + + if ( abs(value_hash) > score_max_eval ) + { + if ( value_hash > 0 ) { value_hash -= ply-1; } + else { value_hash += ply-1; } +#if ! defined(MINIMUM) + if ( abs(value_hash) > score_mate1ply ) + { + out_warning( "Hash value is out of bounce!!" ); + } +#endif + } + + if ( move_hash ) + { + move_hash |= turn_current ? Cap2Move( BOARD[I2To(move_hash)]) + : Cap2Move(-BOARD[I2To(move_hash)]); + } + + if ( turn_hash == turn_current && hand_hash == HAND_B ) { + + assert( ! move_hash || is_move_valid( ptree, move_hash, turn_current ) ); + ptree->amove_hash[ply] = move_hash; + + if ( type_hash == value_lower + && value_hash >= beta + && ( depth_hash >= depth_current || value_hash > score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_lower++; + return value_lower; + } + + if ( type_hash == value_upper + && value_hash <= alpha + && ( depth_hash >= depth_current || value_hash < -score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_upper++; + return value_upper; + } + + if ( type_hash == value_exact + && ( depth_hash >= depth_current + || abs(value_hash) > score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_upper++; + return value_exact; + } + + if ( ( type_hash & flag_value_low_exact ) + && ! ptree->nsuc_check[ply] + && ! ptree->nsuc_check[ply-1] + && ( ( depth_current < 2*PLY_INC + && beta+EFUTIL_MG1 <= value_hash ) + || ( depth_current < 3*PLY_INC + && beta+EFUTIL_MG2 <= value_hash ) ) ) + { + HASH_VALUE = beta; + ptree->ntrans_lower++; + return value_lower; + } + + state_node |= state_node_hash; + + if ( value_hash <= score_max_eval ) { state_node &= ~node_do_mate; } + + if ( ( type_hash & flag_value_up_exact ) + && value_hash < beta + && null_depth <= depth_hash ) + { + state_node &= ~node_do_null; + } + + } else { + + is_superior = eval_supe( HAND_B, hand_hash, turn_current, turn_hash, + &value_hash, &type_hash ); + + if ( is_superior == 1 ) { + + if ( turn_hash == turn_current ) { move_supe = move_hash; } + + if ( type_hash & flag_value_low_exact ) + { + if ( ! ptree->nsuc_check[ply] + && ! ptree->nsuc_check[ply-1] + && ( ( depth_current < 2*PLY_INC + && beta+EFUTIL_MG1 <= value_hash ) + || ( depth_current < 3*PLY_INC + && beta+EFUTIL_MG2 <= value_hash ) ) ) + { + HASH_VALUE = beta; + ptree->ntrans_lower++; + return value_lower; + } + + if ( beta <= value_hash + && ( depth_current <= depth_hash + || score_max_eval < value_hash + || ( turn_current != turn_hash + && depth_hash >= null_depth + && ( state_node & node_do_null ) ) ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_superior_hit++; + return value_lower; + } + } + + } else { + + if ( turn_hash == turn_current ) { move_infe = move_hash; } + + if ( is_superior == -1 ) { + + state_node |= state_node_hash; + + if ( value_hash <= score_max_eval ) { state_node &= ~node_do_mate; } + + if ( type_hash & flag_value_up_exact ) + { + if ( value_hash <= alpha + && ( depth_current <= depth_hash + || value_hash < -score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_inferior_hit++; + return value_upper; + } + if ( value_hash < beta && null_depth <= depth_hash ) + { + state_node &= ~node_do_null; + } + } + } + } + } + } + + slot = (unsigned int)HASH_KEY >> 31; + word1 = ptrans_table[index].always[slot].word1; + word2 = ptrans_table[index].always[slot].word2; + + SignKey( word2, word1 ); + key_hash = word2 & ~(uint64_t)0x7fU; + + if ( key_hash == key_current ) { + + ptree->ntrans_always_hit++; + + depth_hash = (int)((unsigned int)(word1>>56) & 0x00ffU); + value_hash = (int)((unsigned int)(word1>>40) & 0xffffU) - 32768; + move_hash = (unsigned int)(word1>>21) & 0x7ffffU; + hand_hash = (unsigned int)word1 & 0x1fffffU; + + utemp = (unsigned int)word2; + state_node_hash = utemp & node_mate_threat; + turn_hash = (int)((utemp>>6) & 0x1U); + type_hash = (int)((utemp>>3) & 0x3U); + + if ( abs(value_hash) > score_max_eval ) + { + if ( value_hash > 0 ) { value_hash -= ply-1; } + else { value_hash += ply-1; } +#if ! defined(MINIMUM) + if ( abs(value_hash) > score_mate1ply ) + { + out_warning( "Hash value is out of bounce!!" ); + } +#endif + } + + if ( move_hash ) + { + move_hash |= turn_current ? Cap2Move( BOARD[I2To(move_hash)]) + : Cap2Move(-BOARD[I2To(move_hash)]); + } + + if ( turn_hash == turn_current && hand_hash == HAND_B ) { + + if ( ! ptree->amove_hash[ply] ) + { + assert( ! move_hash + || is_move_valid( ptree, move_hash, turn_current ) ); + ptree->amove_hash[ply] = move_hash; + } + + if ( type_hash == value_lower + && value_hash >= beta + && ( depth_hash >= depth_current || value_hash > score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_lower++; + return value_lower; + } + + if ( type_hash == value_upper + && value_hash <= alpha + && ( depth_hash >= depth_current || value_hash < -score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_upper++; + return value_upper; + } + + if ( type_hash == value_exact + && ( depth_hash >= depth_current + || abs(value_hash) > score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_upper++; + return value_exact; + } + + + if ( ( type_hash & flag_value_low_exact ) + && ! ptree->nsuc_check[ply] + && ! ptree->nsuc_check[ply-1] + && ( ( depth_current < 2*PLY_INC + && beta+EFUTIL_MG1 <= value_hash ) + || ( depth_current < 3*PLY_INC + && beta+EFUTIL_MG2 <= value_hash ) ) ) + { + HASH_VALUE = beta; + ptree->ntrans_lower++; + return value_lower; + } + + state_node |= state_node_hash; + + if ( value_hash <= score_max_eval ) { state_node &= ~node_do_mate; } + + if ( ( type_hash & flag_value_up_exact ) + && value_hash < beta + && null_depth <= depth_hash ) + { + state_node &= ~node_do_null; + } + + } else { + + is_superior = eval_supe( HAND_B, hand_hash, turn_current, turn_hash, + &value_hash, &type_hash ); + + if ( is_superior == 1 ) { + + if ( ( turn_hash == turn_current ) && ! move_supe ) + { + move_supe = move_hash; + } + + if ( type_hash & flag_value_low_exact ) + { + if ( ! ptree->nsuc_check[ply] + && ! ptree->nsuc_check[ply-1] + && ( ( depth_current < 2*PLY_INC + && beta+EFUTIL_MG1 <= value_hash ) + || ( depth_current < 3*PLY_INC + && beta+EFUTIL_MG2 <= value_hash ) ) ) + { + HASH_VALUE = beta; + ptree->ntrans_lower++; + return value_lower; + } + + if ( value_hash >= beta + && ( depth_hash >= depth_current + || score_max_eval < value_hash + || ( turn_current != turn_hash + && depth_hash >= null_depth + && ( state_node & node_do_null ) ) ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_superior_hit++; + return value_lower; + } + } + + } else { + + if ( ! move_infe && turn_hash == turn_current ) + { + move_infe = move_hash; + } + + if ( is_superior == -1 ) { + + state_node |= state_node_hash; + + if ( value_hash <= score_max_eval ) { state_node &= ~node_do_mate;} + + if ( type_hash & flag_value_up_exact ) + { + if ( value_hash <= alpha + && ( depth_hash >= depth_current + || value_hash < -score_max_eval ) ) + { + HASH_VALUE = value_hash; + ptree->ntrans_inferior_hit++; + return value_upper; + } + if ( value_hash < beta && null_depth <= depth_hash ) + { + state_node &= ~node_do_null; + } + } + } + } + } + } + + if ( ! ptree->amove_hash[ply] ) + { + if ( move_supe ) + { + ifrom = (int)I2From(move_supe); + if ( ifrom >= nsquare ) + { + unsigned int hand = turn_current ? HAND_W : HAND_B; + switch( From2Drop(ifrom) ) + { + case pawn: + if ( ! IsHandPawn(hand) ) { + move_supe = To2Move(I2To(move_supe)); + if ( IsHandLance(hand) ) + { + move_supe |= Drop2Move(lance); + } + else if ( IsHandSilver(hand)) + { + move_supe |= Drop2Move(silver); + } + else if ( IsHandGold(hand) ) + { + move_supe |= Drop2Move(gold); + } + else { move_supe |= Drop2Move(rook); } + } + break; + + case lance: + if ( ! IsHandLance(hand) ) + { + move_supe = To2Move(I2To(move_supe)) | Drop2Move(rook); + } + break; + } + } + + assert( is_move_valid( ptree, move_supe, turn_current ) ); + ptree->amove_hash[ply] = move_supe; + } + else if ( move_infe ) + { + ifrom = (int)I2From(move_infe); + if ( ifrom >= nsquare ) + { + unsigned int hand = turn_current ? HAND_W : HAND_B; + switch( From2Drop(ifrom) ) + { + case pawn: if ( ! IsHandPawn(hand) ) { goto esc; } break; + case lance: if ( ! IsHandLance(hand) ) { goto esc; } break; + case knight: if ( ! IsHandKnight(hand) ) { goto esc; } break; + case silver: if ( ! IsHandSilver(hand) ) { goto esc; } break; + case gold: if ( ! IsHandGold(hand) ) { goto esc; } break; + case bishop: if ( ! IsHandBishop(hand) ) { goto esc; } break; + case rook: if ( ! IsHandRook(hand) ) { goto esc; } break; + } + } + assert( is_move_valid( ptree, move_infe, turn_current ) ); + ptree->amove_hash[ply] = move_infe; + } + } + + esc: + return state_node; +} + + +int +hash_learn_on( void ) +{ + int iret = file_close( pf_hash ); + if ( iret < 0 ) { return iret; } + + pf_hash = file_open( str_hash, "rb+" ); + if ( pf_hash == NULL ) { return -2; } + + return 1; +} + + +int +hash_learn_off( void ) +{ + int iret = file_close( pf_hash ); + if ( iret < 0 ) { return iret; } + + pf_hash = NULL; + + return 1; +} + +#if !defined(MINIMUM) +int +hash_learn_create( void ) +{ + uint64_t au64[2]; + unsigned int u; + int iret, i; + + iret = hash_learn_off(); + if ( iret < 0 ) { return iret; } + + pf_hash = file_open( str_hash, "wb" ); + if ( pf_hash == NULL ) { return -2; } + + for ( i = 0; i < 5; i++ ) + { + u = 0; + if ( fwrite( &u, sizeof(unsigned int), 1, pf_hash ) != 1 ) + { + str_error = str_io_error; + return -2; + } + } + + u = 0; + au64[0] = au64[1] = 0; + for ( i = 1; i < 0x10000; i++ ) + if ( fwrite( &u, sizeof(unsigned int), 1, pf_hash ) != 1 + || fwrite( au64, sizeof(uint64_t), 2, pf_hash ) != 2 ) + { + str_error = str_io_error; + return -2; + } + + return hash_learn_on(); +} +#endif + +int +hash_learn( const tree_t * restrict ptree, unsigned int move, int value, + int depth ) +{ + trans_entry_t trans_entry; + unsigned int unum, unext, u; + int pre_value, ply; + + ply = record_game.moves; + if ( ply >= HASH_REG_HIST_LEN ) { return 1; } + if ( pf_hash == NULL ) { return 1; } + if ( abs(value) > score_max_eval ) { return 1; } + if ( ply < 2 ) { return 1; } + if ( depth < 2 ) { return 1; } + + if ( history_book_learn[ply].key_probed == (unsigned int)HASH_KEY + && history_book_learn[ply].hand_probed == HAND_B + && history_book_learn[ply].move_probed == move ) { return 1; } + + if ( history_book_learn[ply-2].key_responsible + != history_book_learn[ply-2].key_played ) { return 1; } + if ( history_book_learn[ply-2].hand_responsible + != history_book_learn[ply-2].hand_played ) { return 1; } + if ( history_book_learn[ply-2].move_responsible + != history_book_learn[ply-2].move_played ) { return 1; } + + if ( ( history_book_learn[ply-2].key_probed + == history_book_learn[ply-2].key_played ) + && ( history_book_learn[ply-2].hand_probed + == history_book_learn[ply-2].hand_played ) + && ( history_book_learn[ply-2].move_probed + == history_book_learn[ply-2].move_played ) ) { return 1; } + + pre_value = (int)( history_book_learn[ply-2].data & 0xffffU ) - 32768; + + if ( pre_value < value + HASH_REG_MINDIFF ) { return 1; } + if ( pre_value < -HASH_REG_THRESHOLD ) { return 1; } + if ( pre_value == score_inferior ) { return 1; } + + Out( "save hash value of the position\n\n" ); + if ( fseek( pf_hash, 0, SEEK_SET ) == EOF + || fread( &unum, sizeof(unsigned int), 1, pf_hash ) != 1 + || fread( &unext, sizeof(unsigned int), 1, pf_hash ) != 1 ) + { + str_error = str_io_error; + return -2; + } + if ( ++unum == 0x10000U ) { unum = 0xffffU; } + if ( ++unext == 0x10000U ) { unext = 0x0001U; } + + if ( fseek( pf_hash, 0, SEEK_SET ) == EOF + || fwrite( &unum, sizeof(unsigned int), 1, pf_hash ) != 1 + || fwrite( &unext, sizeof(unsigned int), 1, pf_hash ) != 1 ) + { + str_error = str_io_error; + return -2; + } + trans_entry + = hash_learn_store( ptree, depth * PLY_INC + PLY_INC/2, value, move ); + u = (unsigned int)HASH_KEY; + if ( fseek( pf_hash, (long)( 20 * unext ), SEEK_SET ) == EOF + || fwrite( &u, sizeof(unsigned int), 1, pf_hash ) != 1 + || fwrite( &trans_entry.word1, sizeof(uint64_t), 1, pf_hash ) != 1 + || fwrite( &trans_entry.word2, sizeof(uint64_t), 1, pf_hash ) != 1 + || fflush( pf_hash ) == EOF ) + { + str_error = str_io_error; + return -2; + } + + return 1; +} + + +static int +eval_supe( unsigned int hand_current, unsigned int hand_hash, + int turn_current, int turn_hash, + int * restrict pvalue_hash, int * restrict ptype_hash ) +{ + int is_superior; + + if ( hand_current == hand_hash ) { is_superior = 0; } + else if ( is_hand_eq_supe( hand_current, hand_hash ) ) + { + is_superior = turn_current ? -1 : 1; + } + else if ( is_hand_eq_supe( hand_hash, hand_current ) ) + { + is_superior = turn_current ? 1 : -1; + } + else { return 0; } + + if ( turn_hash != turn_current ) + { + if ( is_superior == -1 ) { is_superior = 0; } + else { + is_superior = 1; + *pvalue_hash *= -1; + switch ( *ptype_hash ) + { + case value_lower: *ptype_hash=value_upper; break; + case value_upper: *ptype_hash=value_lower; break; + } + } + } + + return is_superior; +} + + +int +clear_trans_table( void ) +{ + unsigned int elapsed_start, elapsed_end; + int ntrans_table, i; + + if ( get_elapsed( &elapsed_start ) < 0 ) { return -1; } + + Out( "cleanning the transposition table ..." ); + + trans_table_age = 1; + ntrans_table = 1 << log2_ntrans_table; + for ( i = 0; i < ntrans_table; i++ ) + { + ptrans_table[i].prefer.word1 = 0; + ptrans_table[i].prefer.word2 = 0; + ptrans_table[i].always[0].word1 = 0; + ptrans_table[i].always[0].word2 = 0; + ptrans_table[i].always[1].word1 = 0; + ptrans_table[i].always[1].word2 = 0; + } + + if ( get_elapsed( &elapsed_end ) < 0 ) { return -1; } + Out( " done (%ss)\n", str_time_symple( elapsed_end - elapsed_start ) ); + + return 1; +} + + +void +add_rejections_root( tree_t * restrict ptree, unsigned int move_made ) +{ + uint64_t hash_value; + unsigned int * restrict pmove; + unsigned int *pmove_last; + unsigned int hash_key, hand_ply_turn; + int tt; + unsigned char hash_parent; + + tt = Flip( root_turn ); + UnMakeMove( tt, move_made, 1 ); + hash_parent = (unsigned char)(HASH_KEY >> 32); + + pmove = ptree->amove; + pmove_last = GenCaptures( tt, pmove ); + pmove_last = GenNoCaptures( tt, pmove_last ); + pmove_last = GenCapNoProEx2( tt, pmove_last ); + pmove_last = GenNoCapNoProEx2( tt, pmove_last ); + pmove_last = GenDrop( tt, pmove_last ); + + while ( pmove != pmove_last ) + { + if ( *pmove != move_made ) + { + MakeMove( tt, *pmove, 1 ); + if ( ! InCheck( tt ) ) + { + hash_key = (unsigned int)HASH_KEY & REJEC_MASK; + hand_ply_turn = ( HAND_B << 6 ) | 2U | (unsigned int)tt; + hash_value = ( ( HASH_KEY & ~(uint64_t)0x7ffffffU ) + | (uint64_t)hand_ply_turn ); + hash_rejections[hash_key].root = hash_value; + hash_rejections_parent[hash_key] = hash_parent; + } + UnMakeMove( tt, *pmove, 1 ); + } + pmove++; + } + + MakeMove( tt, move_made, 1 ); +} + + +void +sub_rejections_root( tree_t * restrict ptree, unsigned int move_made ) +{ + unsigned int * restrict pmove; + unsigned int *pmove_last; + unsigned int hash_key; + + pmove = ptree->amove; + pmove_last = GenCaptures( root_turn, pmove ); + pmove_last = GenNoCaptures( root_turn, pmove_last ); + pmove_last = GenCapNoProEx2( root_turn, pmove_last ); + pmove_last = GenNoCapNoProEx2( root_turn, pmove_last ); + pmove_last = GenDrop( root_turn, pmove_last ); + + while ( pmove != pmove_last ) + { + if ( *pmove != move_made ) + { + MakeMove( root_turn, *pmove, 1 ); + if ( ! InCheck( root_turn ) ) + { + hash_key = (unsigned int)HASH_KEY & REJEC_MASK; + + hash_rejections[hash_key].root = 0; + hash_rejections_parent[hash_key] = 0; + } + UnMakeMove( root_turn, *pmove, 1 ); + } + pmove++; + } +} + + +void +add_rejections( tree_t * restrict ptree, int turn, int ply ) +{ + uint64_t hash_value; + unsigned int * restrict pmove; + unsigned int * restrict pmove_last; + unsigned int hash_key, hand_ply_turn; + +#if ! defined(MINIMUM) + if ( game_status & flag_learning ) { return; } +#endif + + pmove = ptree->move_last[ply-1]; + pmove_last = GenCaptures( turn, pmove ); + pmove_last = GenNoCaptures( turn, pmove_last ); + pmove_last = GenCapNoProEx2( turn, pmove_last ); + pmove_last = GenNoCapNoProEx2( turn, pmove_last ); + pmove_last = GenDrop( turn, pmove_last ); + + while ( pmove != pmove_last ) + { + MakeMove( turn, *pmove, ply ); + if ( ! InCheck( turn ) ) + { + hash_key = (unsigned int)HASH_KEY & REJEC_MASK; + if ( ! (unsigned int)hash_rejections[hash_key].sibling ) + { + hand_ply_turn = ( ( HAND_B << 6 ) | ( (unsigned int)ply << 1 ) + | (unsigned int)turn ); + hash_value = ( ( HASH_KEY & ~(uint64_t)0x7ffffffU ) + | (uint64_t)hand_ply_turn ); + hash_rejections[hash_key].sibling = hash_value; +#if defined(TLP) + tlp_rejections_slot[hash_key] = (unsigned short) + ( ptree->tlp_slot ^ (unsigned short)( hash_value >> 32 ) ); +#endif + } + } + UnMakeMove( turn, *pmove, ply ); + pmove++; + } +} + + +void +sub_rejections( tree_t * restrict ptree, int turn, int ply ) +{ + uint64_t hash_value; + unsigned int * restrict pmove; + unsigned int * restrict pmove_last; + unsigned int hash_key, hand_ply_turn; + +#if ! defined(MINIMUM) + if ( game_status & flag_learning ) { return; } +#endif + + pmove = ptree->move_last[ply-1]; + pmove_last = GenCaptures( turn, pmove ); + pmove_last = GenNoCaptures( turn, pmove_last ); + pmove_last = GenCapNoProEx2( turn, pmove_last ); + pmove_last = GenNoCapNoProEx2( turn, pmove_last ); + pmove_last = GenDrop( turn, pmove_last ); + + while ( pmove != pmove_last ) + { + MakeMove( turn, *pmove, ply ); + if ( ! InCheck( turn ) ) + { + hash_key = (unsigned int)HASH_KEY & REJEC_MASK; + hand_ply_turn = ( ( HAND_B << 6 ) + | ( (unsigned int)ply << 1 ) + | (unsigned int)turn ); + hash_value = ( ( HASH_KEY & ~(uint64_t)0x7ffffffU ) + | (uint64_t)hand_ply_turn ); + + if ( hash_rejections[hash_key].sibling == hash_value ) + { + hash_rejections[hash_key].sibling = 0; + } + } + UnMakeMove( turn, *pmove, ply ); + pmove++; + } +} + + +int +rejections_probe( tree_t * restrict ptree, int turn, int ply ) +{ + uint64_t value_turn, value_turn_current, value; + unsigned int hand_hash, hand_current, key_current; + int nrep, value_ply; + unsigned char parent_hash, parent_current; + + turn = Flip(turn); + hand_current = HAND_B; + key_current = (unsigned int)HASH_KEY & REJEC_MASK; + value_turn_current = ( HASH_KEY & ~(uint64_t)0x7ffffffU ) | (uint64_t)turn; + + value = hash_rejections[key_current].root; + value_turn = value & ~(uint64_t)0x7fffffeU; + if ( value_turn == value_turn_current ) + { + hand_hash = ( (unsigned int)value & 0x7ffffffU ) >> 6; + if ( ( turn && is_hand_eq_supe( hand_current, hand_hash ) ) + || ( ! turn && is_hand_eq_supe( hand_hash, hand_current ) ) ) + { + nrep = root_nrep + ply - 2; + parent_current = (unsigned char)(ptree->rep_board_list[nrep] >> 32); + parent_hash = hash_rejections_parent[key_current]; + if ( parent_hash != parent_current ) { return 1; } + } + } + + value = hash_rejections[key_current].sibling; + value_ply = ( (int)value & 0x3eU ) >> 1; + if ( value_ply + 2 < ply ) + { + value_turn = value & ~(uint64_t)0x7fffffeU; + if ( value_turn == value_turn_current ) + { + hand_hash = ( (unsigned int)value & 0x7ffffffU ) >> 6; + if ( ( turn && is_hand_eq_supe( hand_current, hand_hash ) ) + || ( ! turn && is_hand_eq_supe( hand_hash, hand_current ) ) ) + { +#if defined(TLP) + int slot_hash; + slot_hash = (int)( tlp_rejections_slot[key_current] + ^ (unsigned short)( value >> 32 ) ); + if ( tlp_is_descendant( ptree, slot_hash ) ) + +#endif + return 1; + } + } + } + + return 0; +} diff --git a/ini.c b/ini.c new file mode 100644 index 0000000..13db541 --- /dev/null +++ b/ini.c @@ -0,0 +1,1097 @@ +#include +#include +#include +#if ! defined(_WIN32) +# include +#endif +#include "shogi.h" + +#if defined(_MSC_VER) +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) +#else +static int first_one00( int pcs ); +static int last_one00( int pcs ); +#endif + + +static void ini_check_table( void ); +static bitboard_t bb_set_mask( int isquare ); +static int load_fv( void ); +static void set_attacks( int irank, int ifilea, bitboard_t *pbb ); +static void ini_is_same( void ); +static void ini_tables( void ); +static void ini_attack_tables( void ); +static void ini_random_table( void ); + + +static int +load_fv( void ) +{ + FILE *pf; + size_t size; + int iret; + + pf = file_open( str_fv, "rb" ); + if ( pf == NULL ) { return -2; } + + size = nsquare * pos_n; + if ( fread( pc_on_sq, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + size = nsquare * nsquare * kkp_end; + if ( fread( kkp, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + iret = file_close( pf ); + if ( iret < 0 ) { return iret; } + +#if 0 +# define X0 -10000 +# define X1 +10000 + { + unsigned int a[X1-X0+1]; + int i, n, iv; + + for ( i = 0; i < X1-X0+1; i++ ) { a[i] = 0; } + n = nsquare * pos_n; + for ( i = 0; i < n; i++ ) + { + iv = pc_on_sq[0][i]; + if ( iv < X0 ) { iv = X0; } + else if ( iv > X1 ) { iv = X1; } + a[ iv - X0 ] += 1; + } + + pf = file_open( "dist.dat", "w" ); + if ( pf == NULL ) { return -2; } + + for ( i = X0; i <= X1; i++ ) { fprintf( pf, "%d %d\n", i, a[i-X0] ); } + + iret = file_close( pf ); + if ( iret < 0 ) { return iret; } + } +# undef x0 +# undef x1 +#endif + + return 1; +} + +/* +static int +ini_fv( void ) +{ + FILE *pf; + size_t size, i; + + pf = file_open( str_fv, "wb" ); + if ( pf == NULL ) { return -2; } + + size = nsquare * pos_n; + for ( i = 0; i < size; i++ ) { pc_on_sq[0][i] = 0; } + if ( fwrite( pc_on_sq, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + size = nsquare * nsquare * kkp_end; + for ( i = 0; i < size; i++ ) { kkp[0][0][i] = 0; } + if ( fwrite( kkp, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + return file_close( pf ); +} +*/ + +int +ini( tree_t * restrict ptree ) +{ + int i; + + /*if ( ini_fv() < 0 ) { return -1; }*/ + if ( load_fv() < 0 ) { return -1; } + + for ( i = 0; i < 31; i++ ) { p_value[i] = 0; } + for ( i = 0; i < 31; i++ ) { p_value_ex[i] = 0; } + for ( i = 0; i < 15; i++ ) { benefit2promo[i] = 0; } + p_value[15+pawn] = DPawn; + p_value[15+lance] = DLance; + p_value[15+knight] = DKnight; + p_value[15+silver] = DSilver; + p_value[15+gold] = DGold; + p_value[15+bishop] = DBishop; + p_value[15+rook] = DRook; + p_value[15+king] = DKing; + p_value[15+pro_pawn] = DProPawn; + p_value[15+pro_lance] = DProLance; + p_value[15+pro_knight] = DProKnight; + p_value[15+pro_silver] = DProSilver; + p_value[15+horse] = DHorse; + p_value[15+dragon] = DDragon; + + game_status = 0; + str_buffer_cmdline[0] = '\0'; + ptrans_table_orig = NULL; + record_game.pf = NULL; + node_per_second = TIME_CHECK_MIN_NODE; + node_limit = UINT64_MAX; + time_response = TIME_RESPONSE; + sec_limit = 0; + sec_limit_up = 10U; + sec_limit_depth = UINT_MAX; + depth_limit = PLY_MAX; + log2_ntrans_table = 20; + + pf_book = NULL; + pf_hash = NULL; + +#if defined(TLP) + tlp_max = 1; + tlp_abort = 0; + tlp_num = 0; + tlp_idle = 0; + tlp_atree_work[0].tlp_id = 0; + tlp_atree_work[0].tlp_abort = 0; + tlp_atree_work[0].tlp_used = 1; + tlp_atree_work[0].tlp_slot = 0; + tlp_atree_work[0].tlp_nsibling = 0; + if ( lock_init( &tlp_atree_work[0].tlp_lock ) < 0 ) { return -1; } + if ( lock_init( &tlp_lock ) < 0 ) { return -1; } + for ( i = 0; i < tlp_max; i++ ) + { + tlp_atree_work->tlp_ptrees_sibling[i] = NULL; + } + for ( i = 1; i < TLP_NUM_WORK; i++ ) + { + tlp_atree_work[i].tlp_slot = (unsigned short)i; + tlp_atree_work[i].tlp_used = 0; + if ( lock_init( &tlp_atree_work[i].tlp_lock ) < 0 ) { return -1; } + } +# if defined(_WIN32) +# else + if ( pthread_attr_init( &pthread_attr ) + || pthread_attr_setdetachstate( &pthread_attr, + PTHREAD_CREATE_DETACHED ) ) + { + str_error = "pthread_attr_init() failed."; + return -1; + } +# endif +#endif + +#if defined(CSA_LAN) + sckt_csa = SCKT_NULL; + time_last_send = 0U; +#endif + +#if defined(MNJ_LAN) + for ( i = 1; i < MNJ_MASK + 1; i++ ) { mnj_tbl[i] = 0; } + sckt_mnj = SCKT_NULL; + mnj_posi_id = 0; + mnj_move_last = 0; + time_last_send = 0U; +#endif + +#if defined(_WIN32) +# if defined(DEKUNOBOU) + dek_ngame = 0; +# endif +#else + clk_tck = (clock_t)sysconf(_SC_CLK_TCK); +#endif + +#if ! defined(NO_LOGGING) + pf_log = NULL; +#endif + +#if defined(_MSC_VER) +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) +#else + for ( i = 0; i < 0x200; i++ ) + { + aifirst_one[i] = (unsigned char)first_one00(i); + ailast_one[i] = (unsigned char)last_one00(i); + } +#endif + + for ( i = 0; i < HASH_REG_HIST_LEN; i++ ) + { + history_book_learn[i].move_responsible = 0; + history_book_learn[i].move_probed = 0; + history_book_learn[i].move_played = 0; + } + + ini_rand( 5489U ); + ini_is_same(); + ini_tables(); + ini_attack_tables(); + ini_random_table(); + ini_check_table(); + + set_derivative_param(); + + if ( ini_game( ptree, &min_posi_no_handicap, flag_history, NULL, NULL ) < 0 ) + { + return -1; + } + + OutCsaShogi( "%s\n", str_myname ); + Out( "%s\n", str_myname ); + + if ( ini_trans_table() < 0 ) { return -1; } + + if ( book_on() < 0 ) { out_warning( "%s", str_error );} + else { Out( "%s found\n", str_book );} + + if ( hash_learn_on() < 0 ) { out_warning( "%s", str_error );} + else { Out( "%s found\n", str_hash );} + + if ( get_elapsed( &time_turn_start ) < 0 ) { return -1; } + + ini_rand( time_turn_start ); + Out( "rand seed = %x\n", time_turn_start ); + + resign_threshold = RESIGN_THRESHOLD; + +#if defined(MPV) + mpv_num = 1; + mpv_width = 2 * MT_CAP_PAWN; +#endif + + return 1; +} + + +int +fin( void ) +{ +#if defined(TLP) + int i; +#endif + + memory_free( (void *)ptrans_table_orig ); + +#if defined(TLP) + tlp_abort = 1; + while ( tlp_num ) { tlp_yield(); } + if ( lock_free( &tlp_atree_work[0].tlp_lock ) < 0 ) { return -1; } + if ( lock_free( &tlp_lock ) < 0 ) { return -1; } + for ( i = 1; i < TLP_NUM_WORK; i++ ) + { + if ( lock_free( &tlp_atree_work[i].tlp_lock ) < 0 ) { return -1; } + } +# if defined(_WIN32) +# else + if ( pthread_attr_destroy( &pthread_attr ) ) + { + str_error = "pthread_attr_destroy() failed."; + return -1; + } +# endif +#endif + + if ( book_off() < 0 ) { return -1; } + if ( record_close( &record_game ) < 0 ) { return -1; } +#if ! defined(NO_LOGGING) + if ( file_close( pf_log ) < 0 ) { return -1; } +#endif + + return 1; +} + + +void +set_derivative_param( void ) +{ + p_value_ex[15+pawn] = p_value[15+pawn] + p_value[15+pawn]; + p_value_ex[15+lance] = p_value[15+lance] + p_value[15+lance]; + p_value_ex[15+knight] = p_value[15+knight] + p_value[15+knight]; + p_value_ex[15+silver] = p_value[15+silver] + p_value[15+silver]; + p_value_ex[15+gold] = p_value[15+gold] + p_value[15+gold]; + p_value_ex[15+bishop] = p_value[15+bishop] + p_value[15+bishop]; + p_value_ex[15+rook] = p_value[15+rook] + p_value[15+rook]; + p_value_ex[15+king] = p_value[15+king] + p_value[15+king]; + p_value_ex[15+pro_pawn] = p_value[15+pro_pawn] + p_value[15+pawn]; + p_value_ex[15+pro_lance] = p_value[15+pro_lance] + p_value[15+lance]; + p_value_ex[15+pro_knight] = p_value[15+pro_knight] + p_value[15+knight]; + p_value_ex[15+pro_silver] = p_value[15+pro_silver] + p_value[15+silver]; + p_value_ex[15+horse] = p_value[15+horse] + p_value[15+bishop]; + p_value_ex[15+dragon] = p_value[15+dragon] + p_value[15+rook]; + + benefit2promo[7+pawn] = p_value[15+pro_pawn] - p_value[15+pawn]; + benefit2promo[7+lance] = p_value[15+pro_lance] - p_value[15+lance]; + benefit2promo[7+knight] = p_value[15+pro_knight] - p_value[15+knight]; + benefit2promo[7+silver] = p_value[15+pro_silver] - p_value[15+silver]; + benefit2promo[7+bishop] = p_value[15+horse] - p_value[15+bishop]; + benefit2promo[7+rook] = p_value[15+dragon] - p_value[15+rook]; + + p_value[15-pawn] = p_value[15+pawn]; + p_value[15-lance] = p_value[15+lance]; + p_value[15-knight] = p_value[15+knight]; + p_value[15-silver] = p_value[15+silver]; + p_value[15-gold] = p_value[15+gold]; + p_value[15-bishop] = p_value[15+bishop]; + p_value[15-rook] = p_value[15+rook]; + p_value[15-king] = p_value[15+king]; + p_value[15-pro_pawn] = p_value[15+pro_pawn]; + p_value[15-pro_lance] = p_value[15+pro_lance]; + p_value[15-pro_knight] = p_value[15+pro_knight]; + p_value[15-pro_silver] = p_value[15+pro_silver]; + p_value[15-horse] = p_value[15+horse]; + p_value[15-dragon] = p_value[15+dragon]; + + p_value_ex[15-pawn] = p_value_ex[15+pawn]; + p_value_ex[15-lance] = p_value_ex[15+lance]; + p_value_ex[15-knight] = p_value_ex[15+knight]; + p_value_ex[15-silver] = p_value_ex[15+silver]; + p_value_ex[15-gold] = p_value_ex[15+gold]; + p_value_ex[15-bishop] = p_value_ex[15+bishop]; + p_value_ex[15-rook] = p_value_ex[15+rook]; + p_value_ex[15-king] = p_value_ex[15+king]; + p_value_ex[15-pro_pawn] = p_value_ex[15+pro_pawn]; + p_value_ex[15-pro_lance] = p_value_ex[15+pro_lance]; + p_value_ex[15-pro_knight] = p_value_ex[15+pro_knight]; + p_value_ex[15-pro_silver] = p_value_ex[15+pro_silver]; + p_value_ex[15-horse] = p_value_ex[15+horse]; + p_value_ex[15-dragon] = p_value_ex[15+dragon]; + + benefit2promo[7-pawn] = benefit2promo[7+pawn]; + benefit2promo[7-lance] = benefit2promo[7+lance]; + benefit2promo[7-knight] = benefit2promo[7+knight]; + benefit2promo[7-silver] = benefit2promo[7+silver]; + benefit2promo[7-bishop] = benefit2promo[7+bishop]; + benefit2promo[7-rook] = benefit2promo[7+rook]; +} + + +static void +ini_is_same( void ) +{ + int p[16], i, j; + + for ( i = 0; i < 16; i++ ) { p[i] = 0; } + + p[pawn] = 1; + p[lance] = 3; + p[pro_pawn] = 3; + p[knight] = 3; + p[pro_lance] = 3; + p[pro_knight] = 3; + p[silver] = 4; + p[pro_silver] = 4; + p[gold] = 5; + p[bishop] = 6; + p[horse] = 7; + p[rook] = 7; + p[dragon] = 8; + p[king] = 99; + + for ( i = 0; i < 16; i++ ) + for ( j = 0; j < 16; j++ ) + { + if ( p[i] < p[j]-1 ) { is_same[i][j] = 2U; } + else if ( p[i] > p[j]+1 ) { is_same[i][j] = 1U; } + else { is_same[i][j] = 0U; } + } +} + + +static void +ini_tables( void ) +{ + const unsigned char aini_rl90[] = { A1, A2, A3, A4, A5, A6, A7, A8, A9, + B1, B2, B3, B4, B5, B6, B7, B8, B9, + C1, C2, C3, C4, C5, C6, C7, C8, C9, + D1, D2, D3, D4, D5, D6, D7, D8, D9, + E1, E2, E3, E4, E5, E6, E7, E8, E9, + F1, F2, F3, F4, F5, F6, F7, F8, F9, + G1, G2, G3, G4, G5, G6, G7, G8, G9, + H1, H2, H3, H4, H5, H6, H7, H8, H9, + I1, I2, I3, I4, I5, I6, I7, I8, I9 }; + + const unsigned char aini_rl45[] = { A9, B1, C2, D3, E4, F5, G6, H7, I8, + A8, B9, C1, D2, E3, F4, G5, H6, I7, + A7, B8, C9, D1, E2, F3, G4, H5, I6, + A6, B7, C8, D9, E1, F2, G3, H4, I5, + A5, B6, C7, D8, E9, F1, G2, H3, I4, + A4, B5, C6, D7, E8, F9, G1, H2, I3, + A3, B4, C5, D6, E7, F8, G9, H1, I2, + A2, B3, C4, D5, E6, F7, G8, H9, I1, + A1, B2, C3, D4, E5, F6, G7, H8, I9 }; + + const unsigned char aini_rr45[] = { I8, I7, I6, I5, I4, I3, I2, I1, I9, + H7, H6, H5, H4, H3, H2, H1, H9, H8, + G6, G5, G4, G3, G2, G1, G9, G8, G7, + F5, F4, F3, F2, F1, F9, F8, F7, F6, + E4, E3, E2, E1, E9, E8, E7, E6, E5, + D3, D2, D1, D9, D8, D7, D6, D5, D4, + C2, C1, C9, C8, C7, C6, C5, C4, C3, + B1, B9, B8, B7, B6, B5, B4, B3, B2, + A9, A8, A7, A6, A5, A4, A3, A2, A1 }; + bitboard_t abb_plus1dir[ nsquare ]; + bitboard_t abb_plus8dir[ nsquare ]; + bitboard_t abb_plus9dir[ nsquare ]; + bitboard_t abb_plus10dir[ nsquare ]; + bitboard_t abb_minus1dir[ nsquare ]; + bitboard_t abb_minus8dir[ nsquare ]; + bitboard_t abb_minus9dir[ nsquare ]; + bitboard_t abb_minus10dir[ nsquare ]; + bitboard_t bb; + int isquare, i, ito, ifrom, irank, ifile; + int isquare_rl90, isquare_rl45, isquare_rr45; + + for ( isquare = 0; isquare < nsquare; isquare++ ) + { + isquare_rl90 = aini_rl90[isquare]; + isquare_rl45 = aini_rl45[isquare]; + isquare_rr45 = aini_rr45[isquare]; + abb_mask[isquare] = bb_set_mask( isquare ); + abb_mask_rl90[isquare] = bb_set_mask( isquare_rl90 ); + abb_mask_rl45[isquare] = bb_set_mask( isquare_rl45 ); + abb_mask_rr45[isquare] = bb_set_mask( isquare_rr45 ); + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + isquare = irank*nfile + ifile; + BBIni( abb_plus1dir[isquare] ); + BBIni( abb_plus8dir[isquare] ); + BBIni( abb_plus9dir[isquare] ); + BBIni( abb_plus10dir[isquare] ); + BBIni( abb_minus1dir[isquare] ); + BBIni( abb_minus8dir[isquare] ); + BBIni( abb_minus9dir[isquare] ); + BBIni( abb_minus10dir[isquare] ); + for ( i = 1; i < nfile; i++ ) + { + set_attacks( irank, ifile+i, abb_plus1dir + isquare ); + set_attacks( irank+i, ifile-i, abb_plus8dir + isquare ); + set_attacks( irank+i, ifile, abb_plus9dir + isquare ); + set_attacks( irank+i, ifile+i, abb_plus10dir + isquare ); + set_attacks( irank, ifile-i, abb_minus1dir + isquare ); + set_attacks( irank-i, ifile+i, abb_minus8dir + isquare ); + set_attacks( irank-i, ifile, abb_minus9dir + isquare ); + set_attacks( irank-i, ifile-i, abb_minus10dir + isquare ); + } + } + + + for ( isquare = 0; isquare < nsquare; isquare++ ) + { + BBOr( abb_plus_rays[isquare], + abb_plus1dir[isquare], abb_plus8dir[isquare] ); + BBOr( abb_plus_rays[isquare], + abb_plus_rays[isquare], abb_plus9dir[isquare] ); + BBOr( abb_plus_rays[isquare], + abb_plus_rays[isquare], abb_plus10dir[isquare] ); + BBOr( abb_minus_rays[isquare], + abb_minus1dir[isquare], abb_minus8dir[isquare] ); + BBOr( abb_minus_rays[isquare], + abb_minus_rays[isquare], abb_minus9dir[isquare] ); + BBOr( abb_minus_rays[isquare], + abb_minus_rays[isquare], abb_minus10dir[isquare] ); + } + + + for ( ifrom = 0; ifrom < nsquare; ifrom++ ) + { + for ( ito = 0; ito < nsquare; ito++ ) + { + adirec[ifrom][ito] = (unsigned char)direc_misc; + } + + BBOr( bb, abb_plus1dir[ifrom], abb_minus1dir[ifrom] ); + while ( BBToU(bb) ) + { + ito = FirstOne( bb ); + adirec[ifrom][ito] = (unsigned char)direc_rank; + Xor( ito, bb ); + } + BBOr( bb, abb_plus8dir[ifrom], abb_minus8dir[ifrom] ); + while ( BBToU(bb) ) + { + ito = FirstOne( bb ); + adirec[ifrom][ito] = (unsigned char)direc_diag1; + Xor( ito, bb ); + } + BBOr( bb, abb_plus9dir[ifrom], abb_minus9dir[ifrom] ); + while ( BBToU(bb) ) + { + ito = FirstOne( bb ); + adirec[ifrom][ito] = (unsigned char)direc_file; + Xor(ito,bb); + } + BBOr( bb, abb_plus10dir[ifrom], abb_minus10dir[ifrom] ); + while ( BBToU(bb) ) + { + ito = FirstOne( bb ); + adirec[ifrom][ito] = (unsigned char)direc_diag2; + Xor( ito, bb ); + } + } + + for ( ifrom = 0; ifrom < nsquare; ifrom++ ) + for ( ito = 0; ito < nsquare; ito++ ) + { + BBIni( abb_obstacle[ifrom][ito] ); + + if ( ifrom-ito > 0 ) switch ( adirec[ifrom][ito] ) + { + case direc_rank: + BBXor( abb_obstacle[ifrom][ito], + abb_minus1dir[ito+1], abb_minus1dir[ifrom] ); + break; + case direc_file: + BBXor( abb_obstacle[ifrom][ito], + abb_minus9dir[ito+9], abb_minus9dir[ifrom] ); + break; + case direc_diag1: + BBXor( abb_obstacle[ifrom][ito], + abb_minus8dir[ito+8], abb_minus8dir[ifrom] ); + break; + case direc_diag2: + BBXor( abb_obstacle[ifrom][ito], + abb_minus10dir[ito+10], abb_minus10dir[ifrom] ); + break; + } + else switch ( adirec[ifrom][ito] ) + { + case direc_rank: + BBXor( abb_obstacle[ifrom][ito], + abb_plus1dir[ito-1], abb_plus1dir[ifrom] ); + break; + case direc_file: + BBXor( abb_obstacle[ifrom][ito], + abb_plus9dir[ito-9], abb_plus9dir[ifrom] ); + break; + case direc_diag1: + BBXor( abb_obstacle[ifrom][ito], + abb_plus8dir[ito-8], abb_plus8dir[ifrom] ); + break; + case direc_diag2: + BBXor( abb_obstacle[ifrom][ito], + abb_plus10dir[ito-10], abb_plus10dir[ifrom] ); + break; + } + } +} + + +static void +ini_random_table( void ) +{ + int i; + + for ( i = 0; i < nsquare; i++ ) + { + b_pawn_rand[ i ] = rand64(); + b_lance_rand[ i ] = rand64(); + b_knight_rand[ i ] = rand64(); + b_silver_rand[ i ] = rand64(); + b_gold_rand[ i ] = rand64(); + b_bishop_rand[ i ] = rand64(); + b_rook_rand[ i ] = rand64(); + b_king_rand[ i ] = rand64(); + b_pro_pawn_rand[ i ] = rand64(); + b_pro_lance_rand[ i ] = rand64(); + b_pro_knight_rand[ i ] = rand64(); + b_pro_silver_rand[ i ] = rand64(); + b_horse_rand[ i ] = rand64(); + b_dragon_rand[ i ] = rand64(); + w_pawn_rand[ i ] = rand64(); + w_lance_rand[ i ] = rand64(); + w_knight_rand[ i ] = rand64(); + w_silver_rand[ i ] = rand64(); + w_gold_rand[ i ] = rand64(); + w_bishop_rand[ i ] = rand64(); + w_rook_rand[ i ] = rand64(); + w_king_rand[ i ] = rand64(); + w_pro_pawn_rand[ i ] = rand64(); + w_pro_lance_rand[ i ] = rand64(); + w_pro_knight_rand[ i ] = rand64(); + w_pro_silver_rand[ i ] = rand64(); + w_horse_rand[ i ] = rand64(); + w_dragon_rand[ i ] = rand64(); + } + + for ( i = 0; i < npawn_max; i++ ) + { + b_hand_pawn_rand[ i ] = rand64(); + w_hand_pawn_rand[ i ] = rand64(); + } + + for ( i = 0; i < nlance_max; i++ ) + { + b_hand_lance_rand[ i ] = rand64(); + b_hand_knight_rand[ i ] = rand64(); + b_hand_silver_rand[ i ] = rand64(); + b_hand_gold_rand[ i ] = rand64(); + w_hand_lance_rand[ i ] = rand64(); + w_hand_knight_rand[ i ] = rand64(); + w_hand_silver_rand[ i ] = rand64(); + w_hand_gold_rand[ i ] = rand64(); + } + + for ( i = 0; i < nbishop_max; i++ ) + { + b_hand_bishop_rand[ i ] = rand64(); + b_hand_rook_rand[ i ] = rand64(); + w_hand_bishop_rand[ i ] = rand64(); + w_hand_rook_rand[ i ] = rand64(); + } +} + + +static void +ini_attack_tables( void ) +{ + int irank, ifile, pcs, i; + bitboard_t bb; + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + BBIni(bb); + set_attacks( irank-1, ifile-1, &bb ); + set_attacks( irank-1, ifile+1, &bb ); + set_attacks( irank+1, ifile-1, &bb ); + set_attacks( irank+1, ifile+1, &bb ); + set_attacks( irank-1, ifile, &bb ); + abb_b_silver_attacks[ irank*nfile + ifile ] = bb; + + BBIni(bb); + set_attacks( irank-1, ifile-1, &bb ); + set_attacks( irank-1, ifile+1, &bb ); + set_attacks( irank+1, ifile-1, &bb ); + set_attacks( irank+1, ifile+1, &bb ); + set_attacks( irank+1, ifile, &bb ); + abb_w_silver_attacks[ irank*nfile + ifile ] = bb; + + BBIni(bb); + set_attacks( irank-1, ifile-1, &bb ); + set_attacks( irank-1, ifile+1, &bb ); + set_attacks( irank-1, ifile, &bb ); + set_attacks( irank+1, ifile, &bb ); + set_attacks( irank, ifile-1, &bb ); + set_attacks( irank, ifile+1, &bb ); + abb_b_gold_attacks[ irank*nfile + ifile ] = bb; + + BBIni(bb); + set_attacks( irank+1, ifile-1, &bb ); + set_attacks( irank+1, ifile+1, &bb ); + set_attacks( irank+1, ifile, &bb ); + set_attacks( irank-1, ifile, &bb ); + set_attacks( irank, ifile-1, &bb ); + set_attacks( irank, ifile+1, &bb ); + abb_w_gold_attacks[ irank*nfile + ifile ] = bb; + + BBIni(bb); + set_attacks( irank+1, ifile-1, &bb ); + set_attacks( irank+1, ifile+1, &bb ); + set_attacks( irank+1, ifile, &bb ); + set_attacks( irank-1, ifile-1, &bb ); + set_attacks( irank-1, ifile+1, &bb ); + set_attacks( irank-1, ifile, &bb ); + set_attacks( irank, ifile-1, &bb ); + set_attacks( irank, ifile+1, &bb ); + abb_king_attacks[ irank*nfile + ifile ] = bb; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + BBIni(bb); + set_attacks( irank-2, ifile-1, &bb ); + set_attacks( irank-2, ifile+1, &bb ); + abb_b_knight_attacks[ irank*nfile + ifile ] = bb; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + BBIni(bb); + set_attacks( irank+2, ifile-1, &bb ); + set_attacks( irank+2, ifile+1, &bb ); + abb_w_knight_attacks[ irank*nfile + ifile ] = bb; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nrank; ifile++ ) + for ( pcs = 0; pcs < 128; pcs++ ) + { + BBIni(bb); + for ( i = -1; irank+i >= 0; i-- ) + { + set_attacks( irank+i, ifile, &bb ); + if ( (pcs<<1) & (1 << (8-irank-i)) ) { break; } + } + for ( i = 1; irank+i <= 8; i++ ) + { + set_attacks( irank+i, ifile, &bb ); + if ( (pcs<<1) & (1 << (8-irank-i)) ) { break; } + } + abb_file_attacks[irank*nfile+ifile][pcs] = bb; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nrank; ifile++ ) + for ( pcs = 0; pcs < 128; pcs++ ) + { + BBIni(bb); + for ( i = -1; ifile+i >= 0; i-- ) + { + set_attacks( irank, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + for ( i = 1; ifile+i <= 8; i++ ) + { + set_attacks( irank, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + ai_rook_attacks_r0[irank*nfile+ifile][pcs] = bb.p[irank/3]; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nrank; ifile++ ) + for ( pcs = 0; pcs < 128; pcs++ ) + { + BBIni(bb); + if ( ifile <= irank ) + { + for ( i = -1; ifile+i >= 0; i-- ) + { + set_attacks( irank+i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + for ( i = 1; irank+i <= 8; i++ ) + { + set_attacks( irank+i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + } + else { + for ( i = -1; irank+i >= 0; i-- ) + { + set_attacks( irank+i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + for ( i = 1; ifile+i <= 8; i++ ) + { + set_attacks( irank+i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (8-ifile-i)) ) { break; } + } + } + abb_bishop_attacks_rl45[irank*nfile+ifile][pcs] = bb; + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nrank; ifile++ ) + for ( pcs = 0; pcs < 128; pcs++ ) + { + BBIni(bb); + if ( ifile+irank >= 8 ) + { + for ( i = -1; irank-i <= 8; i-- ) + { + set_attacks( irank-i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (irank-i)) ) { break; } + } + for ( i = 1; ifile+i <= 8; i++ ) + { + set_attacks( irank-i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (irank-i)) ) { break; } + } + } + else { + for ( i = -1; ifile+i >= 0; i-- ) + { + set_attacks( irank-i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (irank-i)) ) { break; } + } + for ( i = 1; irank-i >= 0; i++ ) + { + set_attacks( irank-i, ifile+i, &bb ); + if ( (pcs<<1) & (1 << (irank-i)) ) { break; } + } + } + abb_bishop_attacks_rr45[irank*nfile+ifile][pcs] = bb; + } + + for ( i = 0; i < nsquare; i++ ) + { + aslide[i].ir0 = (unsigned char)(i/27); + aslide[i].sr0 = (unsigned char)((2-(i/9)%3)*9+1); + aslide[i].irl90 = (unsigned char)(2-(i%9)/3); + aslide[i].srl90 = (unsigned char)(((i%9)%3)*9+1); + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + if ( irank >= ifile ) + { + aslide[ irank*nfile+ifile ].irl45 + = (unsigned char)((irank-ifile)/3); + aslide[ irank*nfile+ifile ].srl45 + = (unsigned char)((2-((irank-ifile)%3))*9+1); + } + else { + aslide[ irank*nfile+ifile ].irl45 + = (unsigned char)((9+irank-ifile)/3); + aslide[ irank*nfile+ifile ].srl45 + = (unsigned char)((2-((9+irank-ifile)%3))*9+1); + } + } + + for ( irank = 0; irank < nrank; irank++ ) + for ( ifile = 0; ifile < nfile; ifile++ ) + { + if ( ifile+irank >= 8 ) + { + aslide[ irank*nfile+ifile ].irr45 + = (unsigned char)((irank+ifile-8)/3); + aslide[ irank*nfile+ifile ].srr45 + = (unsigned char)((2-((irank+ifile-8)%3))*9+1); + } + else { + aslide[ irank*nfile+ifile ].irr45 + = (unsigned char)((irank+ifile+1)/3); + aslide[ irank*nfile+ifile ].srr45 + = (unsigned char)((2-((irank+ifile+1)%3))*9+1); + } + } +} + + +static void +set_attacks( int irank, int ifile, bitboard_t *pbb ) +{ + if ( irank >= rank1 && irank <= rank9 && ifile >= file1 && ifile <= file9 ) + { + Xor( irank*nfile + ifile, *pbb ); + } +} + + +static bitboard_t +bb_set_mask( int isquare ) +{ + bitboard_t bb; + + if ( isquare > 53 ) + { + bb.p[0] = bb.p[1] = 0; + bb.p[2] = 1U << (80-isquare); + } + else if ( isquare > 26 ) + { + bb.p[0] = bb.p[2] = 0; + bb.p[1] = 1U << (53-isquare); + } + else { + bb.p[1] = bb.p[2] = 0; + bb.p[0] = 1U << (26-isquare); + } + + return bb; +} + + +static void +ini_check_table( void ) +{ + bitboard_t bb_check, bb; + int iking, icheck; + + for ( iking = 0; iking < nsquare; iking++ ) + { + /* black gold */ + BBIni( b_chk_tbl[iking].gold ); + bb_check = abb_w_gold_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( b_chk_tbl[iking].gold, b_chk_tbl[iking].gold, + abb_w_gold_attacks[icheck] ); + Xor( icheck, bb_check ); + } + BBOr( bb, abb_mask[iking], abb_w_gold_attacks[iking] ); + BBNot( bb, bb ); + BBAnd( b_chk_tbl[iking].gold, b_chk_tbl[iking].gold, bb ); + + /* black silver */ + BBIni( b_chk_tbl[iking].silver ); + bb_check = abb_w_silver_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( b_chk_tbl[iking].silver, b_chk_tbl[iking].silver, + abb_w_silver_attacks[icheck] ); + Xor( icheck, bb_check ); + } + bb_check.p[0] = abb_w_gold_attacks[iking].p[0]; + while ( bb_check.p[0] ) + { + icheck = last_one0( bb_check.p[0] ); + BBOr( b_chk_tbl[iking].silver, b_chk_tbl[iking].silver, + abb_w_silver_attacks[icheck] ); + bb_check.p[0] ^= abb_mask[icheck].p[0]; + } + bb_check.p[1] = abb_w_gold_attacks[iking].p[1]; + while ( bb_check.p[1] ) + { + icheck = last_one1( bb_check.p[1] ); + b_chk_tbl[iking].silver.p[0] + |= abb_w_silver_attacks[icheck].p[0]; + bb_check.p[1] ^= abb_mask[icheck].p[1]; + } + BBOr( bb, abb_mask[iking], abb_w_silver_attacks[iking] ); + BBNot( bb, bb ); + BBAnd( b_chk_tbl[iking].silver, b_chk_tbl[iking].silver, bb ); + + /* black knight */ + BBIni( b_chk_tbl[iking].knight ); + bb_check = abb_w_knight_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( b_chk_tbl[iking].knight, b_chk_tbl[iking].knight, + abb_w_knight_attacks[icheck] ); + Xor( icheck, bb_check ); + } + bb_check.p[0] = abb_w_gold_attacks[iking].p[0]; + while ( bb_check.p[0] ) + { + icheck = last_one0( bb_check.p[0] ); + BBOr( b_chk_tbl[iking].knight, b_chk_tbl[iking].knight, + abb_w_knight_attacks[icheck] ); + bb_check.p[0] ^= abb_mask[icheck].p[0]; + } + + /* black lance */ + if ( iking <= I3 ) { + BBAnd( b_chk_tbl[iking].lance, abb_plus_rays[iking+nfile], + abb_file_attacks[iking][0] ); + if ( iking <= I7 && iking != A9 && iking != A8 && iking != A7 ) { + BBAnd( bb, abb_plus_rays[iking-1], abb_file_attacks[iking-1][0] ); + BBOr( b_chk_tbl[iking].lance, b_chk_tbl[iking].lance, bb ); + } + if ( iking <= I7 && iking != I9 && iking != I8 && iking != I7 ) { + BBAnd( bb, abb_plus_rays[iking+1], abb_file_attacks[iking+1][0] ); + BBOr( b_chk_tbl[iking].lance, b_chk_tbl[iking].lance, bb ); + } + } else { BBIni( b_chk_tbl[iking].lance ); } + + /* white gold */ + BBIni( w_chk_tbl[iking].gold ); + bb_check = abb_b_gold_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( w_chk_tbl[iking].gold, w_chk_tbl[iking].gold, + abb_b_gold_attacks[icheck] ); + Xor( icheck, bb_check ); + } + BBOr( bb, abb_mask[iking], abb_b_gold_attacks[iking] ); + BBNot( bb, bb ); + BBAnd( w_chk_tbl[iking].gold, w_chk_tbl[iking].gold, bb ); + + /* white silver */ + BBIni( w_chk_tbl[iking].silver ); + bb_check = abb_b_silver_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( w_chk_tbl[iking].silver, w_chk_tbl[iking].silver, + abb_b_silver_attacks[icheck] ); + Xor( icheck, bb_check ); + } + bb_check.p[2] = abb_b_gold_attacks[iking].p[2]; + while ( bb_check.p[2] ) + { + icheck = first_one2( bb_check.p[2] ); + BBOr( w_chk_tbl[iking].silver, w_chk_tbl[iking].silver, + abb_b_silver_attacks[icheck] ); + bb_check.p[2] ^= abb_mask[icheck].p[2]; + } + bb_check.p[1] = abb_b_gold_attacks[iking].p[1]; + while ( bb_check.p[1] ) + { + icheck = first_one1( bb_check.p[1] ); + w_chk_tbl[iking].silver.p[2] + |= abb_b_silver_attacks[icheck].p[2]; + bb_check.p[1] ^= abb_mask[icheck].p[1]; + } + BBOr( bb, abb_mask[iking], abb_b_silver_attacks[iking] ); + BBNot( bb, bb ); + BBAnd( w_chk_tbl[iking].silver, w_chk_tbl[iking].silver, bb ); + + /* white knight */ + BBIni( w_chk_tbl[iking].knight ); + bb_check = abb_b_knight_attacks[iking]; + while ( BBToU(bb_check) ) + { + icheck = LastOne( bb_check ); + BBOr( w_chk_tbl[iking].knight, w_chk_tbl[iking].knight, + abb_b_knight_attacks[icheck] ); + Xor( icheck, bb_check ); + } + bb_check.p[2] = abb_b_gold_attacks[iking].p[2]; + while ( bb_check.p[2] ) + { + icheck = first_one2( bb_check.p[2] ); + BBOr( w_chk_tbl[iking].knight, w_chk_tbl[iking].knight, + abb_b_knight_attacks[icheck] ); + bb_check.p[2] ^= abb_mask[icheck].p[2]; + } + + /* white lance */ + if ( iking >= A7 ) { + BBAnd( w_chk_tbl[iking].lance, abb_minus_rays[iking-nfile], + abb_file_attacks[iking][0] ); + if ( iking >= A3 && iking != A3 && iking != A2 && iking != A1 ) { + BBAnd( bb, abb_minus_rays[iking-1], abb_file_attacks[iking-1][0] ); + BBOr( w_chk_tbl[iking].lance, w_chk_tbl[iking].lance, bb ); + } + if ( iking >= A3 && iking != I3 && iking != I2 && iking != I1 ) { + BBAnd( bb, abb_minus_rays[iking+1], abb_file_attacks[iking+1][0] ); + BBOr( w_chk_tbl[iking].lance, w_chk_tbl[iking].lance, bb ); + } + } else { BBIni( w_chk_tbl[iking].lance ); } + } +} + + +#if defined(_MSC_VER) +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) +#else +static int +first_one00( int pcs ) +{ + int i; + + for ( i = 0; i < 9; i++ ) { if ( pcs & (1<<(8-i)) ) { break; } } + return i; +} + + +static int +last_one00( int pcs ) +{ + int i; + + for ( i = 8; i >= 0; i-- ) { if ( pcs & (1<<(8-i)) ) { break; } } + return i; +} +#endif diff --git a/io.c b/io.c new file mode 100644 index 0000000..14f841e --- /dev/null +++ b/io.c @@ -0,0 +1,772 @@ +#include +#include +#include +#include +#include +#if defined(_WIN32) +# include +# include +#else +# include +# include +# include +#endif +#include "shogi.h" + +#if defined(_MSC_VER) +# include +# define fopen( file, mode ) _fsopen( file, mode, _SH_DENYNO ) +#endif + +#if defined(NO_STDOUT) || defined(WIN32_PIPE) +static int out_board0( FILE *pf, int piece, int i, int ito, int ifrom ); +# define OutBoard0(a,b,c,d,e,f) out_board0(a,b,c,d,e) +#else +static int out_board0( FILE *pf, int piece, int i, int ito, int ifrom, + int is_promote ); +# define OutBoard0(a,b,c,d,e,f) out_board0(a,b,c,d,e,f) +#endif + +static int check_input_buffer( void ); +static int read_command( char **pstr_line_end ); +static void out_hand( FILE *pf, unsigned int hand, const char *str_prefix ); +static void out_hand0( FILE *pf, int n, const char *str_prefix, + const char *str ); + +#if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) ) +void +out( const char *format, ... ) +{ + va_list arg; + if ( game_status & flag_quiet ) { return; } + +# if ! defined(NO_STDOUT) + va_start( arg, format ); + vprintf( format, arg ); + va_end( arg ); + fflush( stdout ); +# endif + +# if ! defined(NO_LOGGING) + if ( ( strchr( format, '\n' ) != NULL || strchr( format, '\r' ) == NULL ) + && pf_log != NULL ) + { + va_start( arg, format ); + vfprintf( pf_log, format, arg ); + va_end( arg ); + fflush( pf_log ); + } +# endif +} +#endif + + +#if defined(CSASHOGI) +void +out_csashogi( const char *format, ... ) +{ + va_list arg; + + va_start( arg, format ); + vprintf( format, arg ); + va_end( arg ); + + fflush( stdout ); +} +#endif + + +void +out_file( FILE *pf, const char *format, ... ) +{ + va_list arg; + + if ( pf != NULL ) + { + va_start( arg, format ); + vfprintf( pf, format, arg ); + va_end( arg ); + fflush( pf ); + } + +#if ! defined(NO_LOGGING) + if ( pf_log != NULL ) + { + va_start( arg, format ); + vfprintf( pf_log, format, arg ); + va_end( arg ); + fflush( pf_log ); + } +#endif + +} + + +void +out_warning( const char *format, ... ) +{ + va_list arg; + + fprintf( stderr, "\n%s", str_warning ); + va_start( arg, format ); + vfprintf( stderr, format, arg ); + va_end( arg ); + fprintf( stderr, "\n\n" ); + fflush( stderr ); + +#if ! defined(NO_LOGGING) + if ( pf_log != NULL ) + { + fprintf( pf_log, "\n%s", str_warning ); + va_start( arg, format ); + vfprintf( pf_log, format, arg ); + va_end( arg ); + fprintf( pf_log, "\n\n" ); + fflush( pf_log ); + } +#endif + +} + + +void +out_error( const char *format, ... ) +{ + va_list arg; + + fprintf( stderr, "\nERROR: " ); + va_start( arg, format ); + vfprintf( stderr, format, arg ); + va_end( arg ); + fprintf( stderr, "\n\n" ); + fflush( stderr ); + +#if ! defined(NO_LOGGING) + if ( pf_log != NULL ) + { + fprintf( pf_log, "\nERROR: " ); + va_start( arg, format ); + vfprintf( pf_log, format, arg ); + va_end( arg ); + fprintf( pf_log, "\n\n" ); + fflush( pf_log ); + } +#endif + +} + + +FILE * +file_open( const char *str_file, const char *str_mode ) +{ + FILE *pf; + + pf = fopen( str_file, str_mode ); + if ( pf == NULL ) + { + snprintf( str_message, SIZE_MESSAGE, + "%s, %s", str_fopen_error, str_file ); + str_error = str_message; + return NULL; + } + + return pf; +} + + +int +file_close( FILE *pf ) +{ + if ( pf == NULL ) { return 1; } + if ( ferror( pf ) ) + { + fclose( pf ); + str_error = str_io_error; + return -2; + } + if ( fclose( pf ) ) + { + str_error = str_io_error; + return -2; + } + + return 1; +} + + +void +show_prompt( void ) +{ + if ( game_status & flag_noprompt ) { return; } + +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + Out( "Won=%3d Lost=%3d Total=%4d/", dek_win, dek_lost, dek_ngame-1 ); + } +#endif + + if ( game_status & flag_drawn ) { Out( "Drawn> " ); } + else if ( game_status & flag_mated ) + { + if ( root_turn ) { Out( "Black Mated> " ); } + else { Out( "White Mated> " ); } + } + else if ( game_status & flag_resigned ) + { + if ( root_turn ) { Out( "White Resigned> " ); } + else { Out( "Black Resigned> " ); } + } + else if ( game_status & flag_suspend ) + { + if ( root_turn ) { Out( "White Suspend> " ); } + else { Out( "Black Suspend> " ); } + } + else if ( root_turn ) { Out( "White %d> ", record_game.moves+1 ); } + else { Out( "Black %d> ", record_game.moves+1 ); } +} + + +int +open_history( const char *str_name1, const char *str_name2 ) +{ +#if defined(NO_LOGGING) + char str_file[SIZE_FILENAME]; + int iret; + + iret = record_close( &record_game ); + if ( iret < 0 ) { return -1; } + + strncpy( str_file, "game.csa", SIZE_FILENAME-1 ); + iret = record_open( &record_game, str_file, mode_read_write, + str_name1, str_name2 ); + if ( iret < 0 ) { return -1; } + + return 1; +#else + FILE *pf; + int i, iret; + char str_file[SIZE_FILENAME]; + + if ( record_game.pf != NULL && ! record_game.moves ) + { + record_game.str_name1[0] = '\0'; + record_game.str_name2[0] = '\0'; + + if ( str_name1 ) + { + strncpy( record_game.str_name1, str_name1, SIZE_PLAYERNAME-1 ); + record_game.str_name1[SIZE_PLAYERNAME-1] = '\0'; + } + + if ( str_name2 ) + { + strncpy( record_game.str_name2, str_name2, SIZE_PLAYERNAME-1 ); + record_game.str_name2[SIZE_PLAYERNAME-1] = '\0'; + } + return 1; + } + + iret = file_close( pf_log ); + if ( iret < 0 ) { return -1; } + + iret = record_close( &record_game ); + if ( iret < 0 ) { return -1; } + + for ( i = 0; i < 999; i++ ) + { + snprintf( str_file, SIZE_FILENAME, "%s/game%03d.csa", + str_dir_logs, i ); + pf = file_open( str_file, "r" ); + if ( pf == NULL ) { break; } + iret = file_close( pf ); + if ( iret < 0 ) { return -1; } + } + irecord_game = i; + + snprintf( str_file, SIZE_FILENAME, "%s/n%03d.log", + str_dir_logs, i ); + pf_log = file_open( str_file, "w" ); + if ( pf_log == NULL ) { return -1; } + + snprintf( str_file, SIZE_FILENAME, "%s/game%03d.csa", + str_dir_logs, i ); + iret = record_open( &record_game, str_file, mode_read_write, + str_name1, str_name2 ); + if ( iret < 0 ) { return -1; } + + return 1; +#endif +} + + +int +out_board( const tree_t * restrict ptree, FILE *pf, unsigned int move, + int is_strict ) +{ + int irank, ifile, i, iret, ito, ifrom; + +#if ! defined(WIN32_PIPE) + int is_promote; +#endif + + if ( ! is_strict && move ) + { + ito = I2To( move ); + ifrom = I2From( move ); +#if ! defined(NO_STDOUT) && ! defined(WIN32_PIPE) + is_promote = I2IsPromote( move ); +#endif + } + else { + ito = ifrom = nsquare; +#if ! defined(NO_STDOUT) && ! defined(WIN32_PIPE) + is_promote = 0; +#endif + } + + if ( ( game_status & flag_reverse ) && ! is_strict ) + { + fprintf( pf, " \n" ); + fprintf( pf, "' 1 2 3 4 5 6 7 8 9\n" ); + + for ( irank = rank9; irank >= rank1; irank-- ) + { + fprintf( pf, "P%d", irank + 1 ); + + for ( ifile = file9; ifile >= file1; ifile-- ) + { + i = irank * nfile + ifile; + iret = OutBoard0( pf, BOARD[i], i, ito, ifrom, is_promote ); + if ( iret < 0 ) { return iret; } + } + fprintf( pf, "\n" ); + } + } + else { + fprintf( pf, "' 9 8 7 6 5 4 3 2 1\n" ); + + for ( irank = rank1; irank <= rank9; irank++ ) + { + fprintf( pf, "P%d", irank + 1 ); + + for ( ifile = file1; ifile <= file9; ifile++ ) + { + i = irank * nfile + ifile; + iret = OutBoard0( pf, BOARD[i], i, ito, ifrom, is_promote ); + if ( iret < 0 ) { return iret; } + } + fprintf( pf, "\n" ); + } + } + + out_hand( pf, HAND_B, "P+" ); + out_hand( pf, HAND_W, "P-" ); + fflush( pf ); + + return 1; +} + + +int +next_cmdline( int is_wait ) +{ + char *str_line_end; + size_t size; + int iret; + + str_line_end = strchr( str_buffer_cmdline, '\n' ); + + if ( ! str_line_end ) + { + if ( is_wait ) + { + do { + iret = read_command( & str_line_end ); + if ( iret < 0 ) { return iret; } + } while ( ! str_line_end && iret ); + if ( ! iret ) + { + game_status |= flag_quit; + return 1; + } + } + else { +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + iret = dek_check(); + goto tag; + } +#endif + +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) + { + iret = sckt_check( sckt_csa ); + goto tag; + } +#endif + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) + { + iret = sckt_check( sckt_mnj ); + goto tag; + } +#endif + iret = check_input_buffer(); + +#if defined(DEKUNOBOU) || defined(CSA_LAN) || defined(MNJ_LAN) + tag: +#endif + if ( iret <= 0 ) { return iret; } + + iret = read_command( & str_line_end ); + if ( iret < 0 ) { return iret; } + if ( ! iret ) + { + game_status |= flag_quit; + return 1; + } + if ( ! str_line_end ) { return 0; } + } + } + + if ( str_line_end - str_buffer_cmdline + 1 >= SIZE_CMDLINE ) + { + str_error = str_ovrflw_line; + memmove( str_buffer_cmdline, str_line_end + 1, + strlen( str_line_end + 1 ) + 1 ); + return -2; + } + + size = str_line_end - str_buffer_cmdline; + memcpy( str_cmdline, str_buffer_cmdline, size ); + *( str_cmdline + size ) = '\0'; + +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + iret = dek_parse( str_cmdline, SIZE_CMDLINE ); + if ( iret < 0 ) + { + memmove( str_buffer_cmdline, str_line_end + 1, + strlen( str_line_end + 1 ) + 1 ); + return iret; + } + } +#endif + + if ( is_wait ) + { + out_file( NULL, "%s\n", str_cmdline ); + memmove( str_buffer_cmdline, str_line_end + 1, + strlen( str_line_end + 1 ) + 1 ); + } + + return 1; +} + + +static int +#if defined(NO_STDOUT) || defined(WIN32_PIPE) +out_board0( FILE *pf, int piece, int i, int ito, int ifrom ) +#else +out_board0( FILE *pf, int piece, int i, int ito, int ifrom, int is_promote ) +#endif +{ + int ch; +#if ! ( defined(NO_STDOUT) || defined(WIN32_PIPE) ) + int iret; +#endif + + if ( piece ) + { + ch = piece < 0 ? '-' : '+'; +#if ! ( defined(NO_STDOUT) || defined(WIN32_PIPE) ) + if ( i == ito && pf == stdout && ! ( game_status & flag_nostress ) ) + { + iret = StdoutStress( is_promote, ifrom ); + if ( iret < 0 ) + { + fprintf( pf, "\n" ); + return iret; + } + } +#endif + fprintf( pf, "%c%s", ch, astr_table_piece[ abs( piece ) ] ); +#if ! ( defined(NO_STDOUT) || defined(WIN32_PIPE) ) + if ( i == ito && pf == stdout && ! ( game_status & flag_nostress ) ) + { + iret = StdoutNormal(); + if ( iret < 0 ) + { + fprintf( pf, "\n" ); + return iret; + } + } +#endif + } + else { + if ( ifrom < nsquare + && ( ifrom == i + || ( adirec[ito][ifrom] + && adirec[ito][ifrom] == adirec[ito][i] + && ( ( ito < i && i <= ifrom ) + || ( ito > i && i >= ifrom ) ) ) ) ) + { + fprintf( pf, " " ); + } + else { fprintf( pf, " * " ); } + } + + return 1; +} + + +#if ! defined(NO_STDOUT) && ! defined(WIN32_PIPE) + +void +out_beep( void ) +{ + if ( game_status & flag_nobeep ) { return; } + +# if defined(_WIN32) + if ( ! MessageBeep( MB_OK ) ) + { + out_warning( "Beep is not available." ); + } +# else + printf( "\007" ); +# endif +} + + +int +stdout_normal( void ) +{ +# if defined(_WIN32) + HANDLE hStdout; + WORD wAttributes; + + hStdout = GetStdHandle( STD_OUTPUT_HANDLE ); + if ( hStdout == INVALID_HANDLE_VALUE ) + { + str_error = "GetStdHandle() faild"; + return -1; + } + + wAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + if ( ! SetConsoleTextAttribute( hStdout, wAttributes ) ) + { + str_error = "SetConsoleTextAttribute() faild"; + return -1; + } + +# else + printf( "\033[0m" ); +# endif + return 1; +} + + +int +stdout_stress( int is_promote, int ifrom ) +{ +# if defined(_WIN32) + HANDLE hStdout; + WORD wAttributes; + + hStdout = GetStdHandle( STD_OUTPUT_HANDLE ); + if ( hStdout == INVALID_HANDLE_VALUE ) + { + str_error = "GetStdHandle() faild"; + return -1; + } + + if ( is_promote ) + { + wAttributes = BACKGROUND_RED | BACKGROUND_INTENSITY; + } + else if ( ifrom >= nsquare ) + { + wAttributes = BACKGROUND_BLUE | BACKGROUND_INTENSITY; + } + else { + wAttributes = ( BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE + | BACKGROUND_INTENSITY ); + } + if ( ! SetConsoleTextAttribute( hStdout, wAttributes ) ) + { + str_error = "SetConsoleTextAttribute() faild"; + return -1; + } +# else + if ( is_promote ) { printf( "\033[7;31m" ); } + else if ( ifrom >= nsquare ) { printf( "\033[7;34m" ); } + else { printf( "\033[7m" ); } +# endif + + return 1; +} + +#endif /* no NO_STDOUT and no WIN32_PIPE */ + + +static void +out_hand( FILE *pf, unsigned int hand, const char *str_prefix ) +{ + out_hand0( pf, (int)I2HandPawn(hand), str_prefix, "00FU" ); + out_hand0( pf, (int)I2HandLance(hand), str_prefix, "00KY" ); + out_hand0( pf, (int)I2HandKnight(hand), str_prefix, "00KE" ); + out_hand0( pf, (int)I2HandSilver(hand), str_prefix, "00GI" ); + out_hand0( pf, (int)I2HandGold(hand), str_prefix, "00KI" ); + out_hand0( pf, (int)I2HandBishop(hand), str_prefix, "00KA" ); + out_hand0( pf, (int)I2HandRook(hand), str_prefix, "00HI" ); +} + + +static void +out_hand0( FILE *pf, int n, const char *str_prefix, const char *str ) +{ + int i; + + if ( n > 0 ) + { + fprintf( pf, str_prefix ); + for ( i = 0; i < n; i++ ) { fprintf( pf, str ); } + fprintf( pf, "\n" ); + } +} + + +static int +read_command( char ** pstr_line_end ) +{ + char *str_end; + int count_byte, count_cmdbuff; + + count_cmdbuff = (int)strlen( str_buffer_cmdline ); + str_end = str_buffer_cmdline + count_cmdbuff; + +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + count_byte = dek_in( str_end, SIZE_CMDLINE-1-count_cmdbuff ); + if ( count_byte < 0 ) { return count_byte; } + goto tag; + } +#endif + +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) + { + count_byte = sckt_in( sckt_csa, str_end, SIZE_CMDLINE-1-count_cmdbuff ); + if ( count_byte < 0 ) { return count_byte; } + goto tag; + } +#endif + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) + { + count_byte = sckt_in( sckt_mnj, str_end, SIZE_CMDLINE-1-count_cmdbuff ); + if ( count_byte < 0 ) { return count_byte; } + goto tag; + } +#endif + + do { count_byte = (int)read( 0, str_end, SIZE_CMDBUFFER-1-count_cmdbuff ); } + while ( count_byte < 0 && errno == EINTR ); + + if ( count_byte < 0 ) + { + str_error = "read() faild."; + return -1; + } + *( str_end + count_byte ) = '\0'; + +#if defined(DEKUNOBOU) || defined(CSA_LAN) || defined(MNJ_LAN) + tag: +#endif + + + *pstr_line_end = strchr( str_buffer_cmdline, '\n' ); + if ( *pstr_line_end == NULL + && count_byte + count_cmdbuff + 1 >= SIZE_CMDLINE ) + { + *str_buffer_cmdline = '\0'; + str_error = str_ovrflw_line; + return -2; + } + + return count_byte; +} + +#if defined(_WIN32) + +static int +check_input_buffer( void ) +{ +# if defined(WIN32_PIPE) + BOOL bSuccess; + HANDLE hHandle; + DWORD dwBytesRead, dwTotalBytesAvail, dwBytesLeftThisMessage; + char buf[1]; + + hHandle = GetStdHandle( STD_INPUT_HANDLE ); + if ( hHandle == INVALID_HANDLE_VALUE ) + { + str_error = "GetStdHandle() faild."; + return -1; + } + bSuccess = PeekNamedPipe( hHandle, buf, 1, &dwBytesRead, &dwTotalBytesAvail, + &dwBytesLeftThisMessage ); + if ( ! bSuccess ) + { + str_error = "PeekNamedPipe() faild."; + return -1; + } + if ( dwBytesRead ) { return 1; } + return 0; +# else + return _kbhit(); +# endif +} + +#else /* no _WIN32 */ + +static int +check_input_buffer( void ) +{ + fd_set readfds; + struct timeval tv; + int iret; + +#if defined(__ICC) +# pragma warning(disable:279) +# pragma warning(disable:593) +# pragma warning(disable:1469) +#endif + + FD_ZERO(&readfds); + FD_SET(0, &readfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + iret = select( 1, &readfds, NULL, NULL, &tv ); + if ( iret == -1 ) + { + str_error = "select() faild."; + return -1; + } + return iret; + +#if defined(__ICC) +# pragma warning(default:279) +# pragma warning(default:593) +# pragma warning(default:1469) +#endif +} + +#endif /* no _WIN32 */ diff --git a/iterate.c b/iterate.c new file mode 100644 index 0000000..d940814 --- /dev/null +++ b/iterate.c @@ -0,0 +1,871 @@ +#include +#include +#include +#include +#include "shogi.h" + +static void adjust_fmg( void ); +static int ini_hash( void ); +static int set_root_alpha( int nfail_low, int root_alpha_old ); +static int set_root_beta( int nfail_high, int root_beta_old ); +static int is_answer_right( unsigned int move ); +static const char *str_fail_high( int turn, int nfail_high ); + + +static int rep_book_prob( tree_t * restrict ptree ) +{ + int i; + + for ( i = root_nrep - 2; i >= 0; i -= 2 ) + if ( ptree->rep_board_list[i] == HASH_KEY + && ptree->rep_hand_list[i] == HAND_B ) + { + Out( "- book is ignored due to a repetition.\n" ); + return 1; + } + + return 0; +} + + +int +iterate( tree_t * restrict ptree, int flag ) +{ + int value, iret, ply, is_hash_learn_stored; + unsigned int cpu_start; + int right_answer_made; + + /* probe the opening book */ + if ( pf_book != NULL && n_nobook_move < 7 && ! rep_book_prob( ptree ) ) + { + int is_book_hit, i; + unsigned int elapsed; + + is_book_hit = book_probe( ptree ); + if ( is_book_hit < 0 ) { return is_book_hit; } + + iret = get_elapsed( &elapsed ); + if ( iret < 0 ) { return iret; } + + Out( "- opening book is probed. (%ss)\n", + str_time_symple( elapsed - time_start ) ); + if ( is_book_hit ) + { + pv_close( ptree, 2, book_hit ); + last_pv = ptree->pv[1]; + last_root_value = 0; + n_nobook_move = 0; + if ( ! ( game_status & flag_puzzling ) ) + { + for ( i = 0; i < HIST_SIZE; i++ ) + { + ptree->hist_good[i] /= 256U; + ptree->hist_tried[i] /= 256U; + } + } + + MnjOut( "pid=%d move=%s v=0b n=0 confident\n", mnj_posi_id, + str_CSA_move(ptree->pv[1].a[1]) ); + + return 1; + } + if ( ! ( game_status & ( flag_puzzling | flag_pondering ) ) ) + { + n_nobook_move += 1; + } + } + + /* initialize variables */ + if ( get_cputime( &cpu_start ) < 0 ) { return -1; } + + ptree->node_searched = 0; + ptree->nreject_done = 0; + ptree->nreject_tried = 0; + ptree->null_pruning_done = 0; + ptree->null_pruning_tried = 0; + ptree->check_extension_done = 0; + ptree->recap_extension_done = 0; + ptree->onerp_extension_done = 0; + ptree->nfour_fold_rep = 0; + ptree->nperpetual_check = 0; + ptree->nsuperior_rep = 0; + ptree->nrep_tried = 0; + ptree->neval_called = 0; + ptree->nquies_called = 0; + ptree->ntrans_always_hit = 0; + ptree->ntrans_prefer_hit = 0; + ptree->ntrans_probe = 0; + ptree->ntrans_exact = 0; + ptree->ntrans_lower = 0; + ptree->ntrans_upper = 0; + ptree->ntrans_superior_hit = 0; + ptree->ntrans_inferior_hit = 0; + ptree->fail_high = 0; + ptree->fail_high_first = 0; + ptree->current_move[0] = 0; + ptree->pv[0].a[0] = 0; + ptree->pv[0].a[1] = 0; + ptree->pv[0].depth = 0; + ptree->pv[0].length = 0; + iteration_depth = 0; + easy_value = 0; + easy_abs = 0; + right_answer_made = 0; + is_hash_learn_stored = 0; + root_abort = 0; + root_nmove = 0; + root_value = -score_bound; + root_alpha = -score_bound; + root_beta = score_bound; + root_move_cap = 0; + node_last_check = 0; + time_last_eff_search = time_start; + time_last_check = time_start; + game_status &= ~( flag_move_now | flag_suspend + | flag_quit_ponder | flag_search_error ); +#if defined(DBG_EASY) + easy_move = 0; +#endif + +#if defined(TLP) + ptree->tlp_abort = 0; + tlp_nsplit = 0; + tlp_nabort = 0; + tlp_nslot = 0; +#endif + +#if defined(MPV) + if ( ! ( game_status & flag_puzzling ) && mpv_num > 1 ) + { + int i; + + for ( i = 0; i < 2*mpv_num+1; i++ ) { mpv_pv[i].length = 0; } + + last_pv.a[0] = 0; + last_pv.a[1] = 0; + last_pv.depth = 0; + last_pv.length = 0; + last_root_value = 0; + + root_mpv = 1; + } + else { root_mpv = 0; } +#endif + + + for ( ply = 0; ply < PLY_MAX; ply++ ) + { + ptree->amove_killer[ply].no1 = ptree->amove_killer[ply].no2 = 0U; + ptree->killers[ply].no1 = ptree->killers[ply].no2 = 0x0U; + } + + { + unsigned int u = node_per_second / 16U; + if ( u > TIME_CHECK_MAX_NODE ) { u = TIME_CHECK_MAX_NODE; } + else if ( u < TIME_CHECK_MIN_NODE ) { u = TIME_CHECK_MIN_NODE; } + node_next_signal = u; + } + + set_search_limit_time( root_turn ); + adjust_fmg(); + + /* look up last pv. */ + if ( last_pv.length ) + { + Out( "- a pv was found in the previous search result.\n" ); + + iteration_depth = last_pv.depth; + ptree->pv[0] = last_pv; + ptree->pv[0].type = prev_solution; + root_value = root_turn ? -last_root_value : last_root_value; + out_pv( ptree, root_value, root_turn, 0 ); + Out( "\n" ); + } + + /* probe the transposition table, since last pv is not available. */ + if ( ! last_pv.length +#if defined(MPV) + && ! root_mpv +#endif + ) + { + unsigned int value_type; + int alpha, beta; + + iret = ini_hash(); + if ( iret < 0 ) { return iret; } + is_hash_learn_stored = 1; + + value = INT_MIN; + for ( ply = 1; ply < PLY_MAX - 10; ply++ ) + { + alpha = -score_bound; + beta = score_bound; + + value_type = hash_probe( ptree, 1, ply*PLY_INC+PLY_INC/2, + root_turn, alpha, beta, 0 ); + if ( value_type != value_exact ) { break; } + value = HASH_VALUE; + } + + if ( -score_bound < value ) + { + Out( "- a pv was peeked through the transposition table.\n" ); + iteration_depth = ply-1; + ptree->pv[0].depth = (unsigned char)(ply-1); + ptree->pv[0].type = hash_hit; + root_value = value; + out_pv( ptree, value, root_turn, 0 ); + Out( "\n" ); + + if ( ! ptree->pv[0].length ) + { + iteration_depth = 0; + ptree->pv[0].depth = 0; + root_value = -score_bound; +#if ! defined(MINIMUM) + out_warning( "PEEK FAILED!!!" ); +#endif + } + } + } + + /* root move generation */ + { + unsigned int elapsed; + + Out( "- root move generation" ); + value = make_root_move_list( ptree, flag ); + if ( game_status & flag_search_error ) { return -1; } + if ( game_status & ( flag_quit | flag_quit_ponder | flag_suspend ) ) + { + return 1; + } + + if ( ! root_nmove ) + { + str_error = "No legal moves to search"; + return -2; + } + + if ( ! ptree->pv[0].length || ptree->pv[0].a[1] != root_move_list[0].move ) + { + iteration_depth = 0; + ptree->pv[0].a[1] = root_move_list[0].move; + ptree->pv[0].length = 1; + ptree->pv[0].depth = 1; + ptree->pv[0].type = no_rep; + root_value = value; + } + +#if defined(MPV) + if ( root_mpv ) + { + if ( root_nmove == 1 ) { root_mpv = 0; } + easy_abs = 0; + } +#endif + + if ( get_elapsed( &elapsed ) < 0 ) { return -1; } + Out( " ... done (%d moves, %ss)\n", + root_nmove, str_time_symple( elapsed - time_start ) ); + } + + + /* save preliminary result */ + assert( root_value != -score_bound ); + last_root_value = root_turn ? -root_value : root_value; + last_pv = ptree->pv[0]; + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) + { + const char *str = ( root_nmove == 1 ) ? "confident" : ""; + + MnjOut( "pid=%d move=%s v=%de n=%" PRIu64 " %s\n", + mnj_posi_id, str_CSA_move(ptree->pv[0].a[1]), root_value, + ptree->node_searched, str ); + } +#endif + + /* return, if previous pv is long enough */ + if ( abs(root_value) > score_max_eval + || iteration_depth >= depth_limit + || ( ( game_status & flag_puzzling ) + && ( root_nmove == 1 || ptree->pv[0].depth > 4 ) ) ) + { + return 1; + } + + if ( ! is_hash_learn_stored ) + { + iret = ini_hash(); + if ( iret < 0 ) { return iret; } + } + + /* iterative deepening search */ +#if defined(TLP) + iret = tlp_start(); + if ( iret < 0 ) { return iret; } +#endif + iteration_depth += 1; + root_beta = set_root_beta( 0, root_value ); + root_alpha = set_root_alpha( 0, root_value ); + root_value = root_alpha; + add_rejections( ptree, root_turn, 1 ); + Out( "- drive an iterative deepening search starting from depth %d\n", + iteration_depth ); + + for ( ; iteration_depth < 30/*PLY_MAX-10*/; iteration_depth++ ) { + + MnjOut( "pid=%d d=%d\n", mnj_posi_id, iteration_depth ); + + + if ( get_elapsed( &time_last_search ) < 0 ) { return -1; } + +#if defined(MPV) + if ( root_mpv ) + { + int i; + i = ( root_nmove < mpv_num ) ? root_nmove : mpv_num; + for ( ; i < mpv_num*2; i++ ) { mpv_pv[i].length = 0; } + } +#endif + + { + unsigned int move; + int tt, i, n; + + tt = root_turn; + for ( ply = 1; ply <= ptree->pv[0].length; ply++ ) + { + move = ptree->pv[0].a[ply]; + if ( ! is_move_valid( ptree, move, tt ) ) + { +#if ! defined(MINIMUM) + out_warning( "Old pv has an illegal move! ply=%d, move=%s", + ply, str_CSA_move(move) ); +#endif + break; + } + MakeMove( tt, move, ply ); + if ( InCheck(tt) ) + { +#if ! defined(MINIMUM) + out_warning( "Old pv has an illegal evasion! ply=%d, move=%s", + ply, str_CSA_move(move) ); +#endif + UnMakeMove( tt, move, ply ); + break; + } + tt = Flip(tt); + } + for ( ply--; ply > 0; ply-- ) + { + tt = Flip(tt); + move = ptree->pv[0].a[ply]; + UnMakeMove( tt, move, ply ); + hash_store_pv( ptree, move, tt ); + } + + root_nfail_high = 0; + root_nfail_low = 0; + + n = root_nmove; + root_move_list[0].status = flag_first; + for ( i = 1; i < n; i++ ) { root_move_list[i].status = 0; } + } + + /* a trial of searches */ + for ( ;; ) { + value = searchr( ptree, root_alpha, root_beta, root_turn, + iteration_depth*PLY_INC + PLY_INC/2 ); + if ( game_status & flag_search_error ) { return -1; } + if ( root_abort ) { break; } + + assert( abs(value) < score_foul ); + + if ( root_beta <= value ) + { + const char *str_move; + const char *str; + double dvalue; + + root_move_list[0].status &= ~flag_searched; + root_move_list[0].status |= flag_failhigh; + dvalue = (double)( root_turn ? -root_beta : root_beta ); + + do { root_beta = set_root_beta( ++root_nfail_high, root_beta ); } + while ( value >= root_beta ); + + str = str_time_symple( time_last_result - time_start ); + if ( root_move_list[0].status & flag_first ) + { + Out( "(%2d)%6s %7.2f ", iteration_depth, str, dvalue / 100.0 ); + } + else { Out( " %6s %7.2f ", str, dvalue / 100.0 ); } + + str = str_fail_high( root_turn, root_nfail_high ); + + MnjOut( "pid=%d move=%s v=%dl n=%" PRIu64 "\n", mnj_posi_id, + str_CSA_move(ptree->pv[1].a[1]), root_beta, + ptree->node_searched ); + + str_move = str_CSA_move_plus( ptree, ptree->pv[1].a[1], 1, + root_turn ); + Out( " 1.%c%s [%s!]\n", ach_turn[root_turn], str_move, str ); + + + if ( game_status & flag_pondering ) + { + OutCsaShogi( "info%+.2f %c%s %c%s [%s!]\n", + dvalue / 100.0, ach_turn[Flip(root_turn)], + str_CSA_move(ponder_move), + ach_turn[root_turn], str_move, str ); + } + else { + OutCsaShogi( "info%+.2f %c%s [%s!]\n", dvalue / 100.0, + ach_turn[root_turn], str_move, str ); + } + } + else if ( value <= root_alpha ) + { + const char *str_move; + const char *str; + unsigned int time_elapsed; + double dvalue; + + if ( ! ( root_move_list[0].status & flag_first ) ) + { + root_value = root_alpha; + break; + } + + root_move_list[0].status &= ~flag_searched; + root_move_list[0].status |= flag_faillow; + dvalue = (double)( root_turn ? -root_alpha : root_alpha ); + + if ( get_elapsed( &time_elapsed ) < 0 ) { return -1; } + + do { root_alpha = set_root_alpha( ++root_nfail_low, root_alpha ); } + while ( value <= root_alpha ); + root_value = root_alpha; + str = str_time_symple( time_elapsed - time_start ); + Out( "(%2d)%6s %7.2f ", iteration_depth, str, dvalue / 100.0 ); + + str = str_fail_high( Flip(root_turn), root_nfail_low ); + str_move = str_CSA_move_plus( ptree, root_move_list[0].move, 1, + root_turn ); + Out( " 1.%c%s [%s?]\n", ach_turn[root_turn], str_move, str ); + if ( game_status & flag_pondering ) + { + OutCsaShogi( "info%+.2f %c%s %c%s [%s?]\n", + dvalue / 100.0, ach_turn[Flip(root_turn)], + str_CSA_move(ponder_move), + ach_turn[root_turn], str_move, str ); + } + else { + OutCsaShogi( "info%+.2f %c%s [%s?]\n", dvalue / 100.0, + ach_turn[root_turn], str_move, str ); + } + } + else { break; } + } + + /* the trial of search ended */ + if ( root_alpha < root_value && root_value < root_beta ) + { + last_root_value = root_turn ? - root_value : root_value; + last_pv = ptree->pv[0]; + } + + if ( root_abort ) { break; } + + if ( root_alpha < root_value && root_value < root_beta ) + { +#if ! defined(MINIMUM) + { + int i, n; + n = root_nmove; + for ( i = 0; i < n; i++ ) + { + if ( root_move_list[i].status & flag_searched ) { continue; } + out_warning( "A root move %s is ignored\n", + str_CSA_move(root_move_list[i].move) ); + } + } +#endif + + if ( ( game_status & flag_problem ) && depth_limit == PLY_MAX ) + { + if ( is_answer_right( ptree->pv[0].a[1] ) ) + { + if ( right_answer_made > 1 && iteration_depth > 3 ) { break; } + right_answer_made++; + } + else { right_answer_made = 0; } + } + + if ( abs(value) > score_max_eval ) { break; } + if ( iteration_depth >= depth_limit ) { break; } + + root_beta = set_root_beta( 0, value ); + root_alpha = set_root_alpha( 0, value ); + root_value = root_alpha; + } +#if ! defined(MINIMUM) + else { out_warning(( "SEARCH INSTABILITY IS DETECTED!!!" )); } +#endif + + /* shell sort */ + { + root_move_t root_move_swap; + const int n = root_nmove; + uint64_t sortv; + int i, j, k, h; + + for ( k = SHELL_H_LEN - 1; k >= 0; k-- ) + { + h = ashell_h[k]; + for ( i = n-h-1; i > 0; i-- ) + { + root_move_swap = root_move_list[i]; + sortv = root_move_list[i].nodes; + for ( j = i+h; j < n && root_move_list[j].nodes > sortv; j += h ) + { + root_move_list[j-h] = root_move_list[j]; + } + root_move_list[j-h] = root_move_swap; + } + } + } + } + + /* iteration ended */ + sub_rejections( ptree, root_turn, 1 ); + + if ( game_status & flag_problem ) + { + if ( is_answer_right( ptree->pv[0].a[1] ) ) + { + right_answer_made = 1; + } + else { right_answer_made = 0; } + } + + { + int i; + + for ( i = 0; i < HIST_SIZE; i++ ) + { + ptree->hist_good[i] /= 256U; + ptree->hist_tried[i] /= 256U; + } + } + /* prunings and extentions-statistics */ + { + double drep, dreject, dhash, dnull, dfh1st; + + drep = (double)ptree->nperpetual_check; + drep += (double)ptree->nfour_fold_rep; + drep += (double)ptree->nsuperior_rep; + drep *= 100.0 / (double)( ptree->nrep_tried + 1 ); + + dreject = 100.0 * (double)ptree->nreject_done; + dreject /= (double)( ptree->nreject_tried + 1 ); + + dhash = (double)ptree->ntrans_exact; + dhash += (double)ptree->ntrans_inferior_hit; + dhash += (double)ptree->ntrans_superior_hit; + dhash += (double)ptree->ntrans_upper; + dhash += (double)ptree->ntrans_lower; + dhash *= 100.0 / (double)( ptree->ntrans_probe + 1 ); + + dnull = 100.0 * (double)ptree->null_pruning_done; + dnull /= (double)( ptree->null_pruning_tried + 1 ); + + dfh1st = 100.0 * (double)ptree->fail_high_first; + dfh1st /= (double)( ptree->fail_high + 1 ); + + Out( " pruning -> rep=%4.2f%% reject=%4.2f%%\n", drep, dreject ); + + Out( " pruning -> hash=%2.0f%% null=%2.0f%% fh1st=%4.1f%%\n", + dhash, dnull, dfh1st ); + + Out( " extension-> chk=%u recap=%u 1rep=%u\n", + ptree->check_extension_done, ptree->recap_extension_done, + ptree->onerp_extension_done ); + } + + /* futility threashold */ +#if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) ) + { + int misc = fmg_misc; + int drop = fmg_drop; + int cap = fmg_cap; + int mt = fmg_mt; + int misc_k = fmg_misc_king; + int cap_k = fmg_cap_king; + Out( " futility -> misc=%d drop=%d cap=%d mt=%d misc(k)=%d cap(k)=%d\n", + misc, drop, cap, mt, misc_k, cap_k ); + } +#endif + + /* hashing-statistics */ + { + double dalways, dprefer, dsupe, dinfe; + double dlower, dupper, dsat; + uint64_t word2; + int ntrans_table, i, n; + + ntrans_table = 1 << log2_ntrans_table; + if ( ntrans_table > 8192 ) { ntrans_table = 8192; } + + for ( i = 0, n = 0; i < ntrans_table; i++ ) + { + word2 = ptrans_table[i].prefer.word2; + SignKey( word2, ptrans_table[i].prefer.word1 ); + if ( trans_table_age == ( 7 & (int)word2 ) ) { n++; } + + word2 = ptrans_table[i].always[0].word2; + SignKey( word2, ptrans_table[i].always[0].word1 ); + if ( trans_table_age == ( 7 & (int)word2 ) ) { n++; } + + word2 = ptrans_table[i].always[1].word2; + SignKey( word2, ptrans_table[i].always[1].word1 ); + if ( trans_table_age == ( 7 & (int)word2 ) ) { n++; } + } + + dalways = 100.0 * (double)ptree->ntrans_always_hit; + dalways /= (double)( ptree->ntrans_probe + 1 ); + + dprefer = 100.0 * (double)ptree->ntrans_prefer_hit; + dprefer /= (double)( ptree->ntrans_probe + 1 ); + + dsupe = 100.0 * (double)ptree->ntrans_superior_hit; + dsupe /= (double)( ptree->ntrans_probe + 1 ); + + dinfe = 100.0 * (double)ptree->ntrans_inferior_hit; + dinfe /= (double)( ptree->ntrans_probe + 1 ); + + Out( " hashing -> always=%2.0f%% prefer=%2.0f%% supe=%4.2f%% " + "infe=%4.2f%%\n", dalways, dprefer, dsupe, dinfe ); + + dlower = 100.0 * (double)ptree->ntrans_lower; + dlower /= (double)( ptree->ntrans_probe + 1 ); + + dupper = 100.0 * (double)ptree->ntrans_upper; + dupper /= (double)( ptree->ntrans_probe + 1 ); + + dsat = 100.0 * (double)n; + dsat /= (double)( 3 * ntrans_table ); + + OutCsaShogi( "statsatu=%.0f", dsat ); + Out( " hashing -> " + "exact=%d lower=%2.0f%% upper=%4.2f%% sat=%2.0f%% age=%d\n", + ptree->ntrans_exact, dlower, dupper, dsat, trans_table_age ); + if ( dsat > 9.0 ) { trans_table_age = ( trans_table_age + 1 ) & 0x7; } + } + +#if defined(TLP) + if ( tlp_max > 1 ) + { + Out( " threading-> split=%d abort=%d slot=%d\n", + tlp_nsplit, tlp_nabort, tlp_nslot+1 ); + if ( tlp_nslot+1 == TLP_NUM_WORK ) + { + out_warning( "THREAD WORK AREA IS USED UP!!!" ); + } + } +#endif + + { + double dcpu_percent, dnps, dmat; + unsigned int cpu, elapsed; + + Out( " n=%" PRIu64 " quies=%u eval=%u rep=%u %u(chk) %u(supe)\n", + ptree->node_searched, ptree->nquies_called, ptree->neval_called, + ptree->nfour_fold_rep, ptree->nperpetual_check, + ptree->nsuperior_rep ); + + if ( get_cputime( &cpu ) < 0 ) { return -1; } + if ( get_elapsed( &elapsed ) < 0 ) { return -1; } + + cpu -= cpu_start; + elapsed -= time_start; + + dcpu_percent = 100.0 * (double)cpu; + dcpu_percent /= (double)( elapsed + 1U ); + + dnps = 1000.0 * (double)ptree->node_searched; + dnps /= (double)( elapsed + 1U ); + +#if defined(TLP) + { + double n = (double)tlp_max; + node_per_second = (unsigned int)( ( dnps + 0.5 ) / n ); + } +#else + node_per_second = (unsigned int)( dnps + 0.5 ); +#endif + + dmat = (double)MATERIAL; + dmat /= (double)MT_CAP_PAWN; + + OutCsaShogi( " cpu=%.0f nps=%.2f\n", dcpu_percent, dnps / 1e3 ); + Out( " time=%s ", str_time_symple( elapsed ) ); + Out( "cpu=%3.0f%% mat=%.1f nps=%.2fK", dcpu_percent, dmat, dnps / 1e3 ); + Out( " time_eff=%s\n\n", + str_time_symple( time_last_eff_search - time_start ) ); + } + + if ( ( game_status & flag_problem ) && ! right_answer_made ) { iret = 0; } + else { iret = 1; } + + return iret; +} + + +static int +ini_hash( void ) +{ + unsigned int elapsed; + int iret; + + if ( time_limit < 150U ) { return 1; } + + iret = all_hash_learn_store(); + if ( iret < 0 ) { return iret; } + if ( iret ) + { + if ( get_elapsed( &elapsed ) < 0 ) { return -1; } + Out( "- load learnt hash values (%ss)\n", + str_time_symple( elapsed - time_start ) ); + } + + return 1; +} + + +static void +adjust_fmg( void ) +{ + int misc, cap, drop, mt, misc_king, cap_king; + + misc = fmg_misc - FMG_MG / 2; + cap = fmg_cap - FMG_MG / 2; + drop = fmg_drop - FMG_MG / 2; + misc_king = fmg_misc_king - FMG_MG_KING / 2; + cap_king = fmg_cap_king - FMG_MG_KING / 2; + mt = fmg_mt - FMG_MG_MT / 2; + + fmg_misc = ( misc < FMG_MISC ) ? FMG_MISC : misc; + fmg_cap = ( cap < FMG_CAP ) ? FMG_CAP : cap; + fmg_drop = ( drop < FMG_DROP ) ? FMG_DROP : drop; + fmg_misc_king = ( misc_king < FMG_MISC_KING ) ? FMG_MISC_KING : misc_king; + fmg_cap_king = ( cap_king < FMG_CAP_KING ) ? FMG_CAP_KING : cap_king; + fmg_mt = ( mt < FMG_MT ) ? FMG_MT : mt; +} + + +static int +set_root_beta( int nfail_high, int root_beta_old ) +{ + int aspiration_hwdth, aspiration_fail1; + + if ( time_max_limit != time_limit ) + { + aspiration_hwdth = MT_CAP_DRAGON / 8; + aspiration_fail1 = MT_CAP_DRAGON / 2; + } + else { + aspiration_hwdth = MT_CAP_DRAGON / 4; + aspiration_fail1 = ( MT_CAP_DRAGON * 3 ) / 4; + } + + switch ( nfail_high ) + { + case 0: root_beta_old += aspiration_hwdth; break; + case 1: root_beta_old += aspiration_fail1 - aspiration_hwdth; break; + case 2: root_beta_old = score_bound; break; + default: + out_error( "Error at set_root_beta!" ); + exit(1); + } + if ( root_beta_old > score_max_eval ) { root_beta_old = score_bound; } + + return root_beta_old; +} + + +static int +set_root_alpha( int nfail_low, int root_alpha_old ) +{ + int aspiration_hwdth, aspiration_fail1; + + if ( time_max_limit != time_limit ) + { + aspiration_hwdth = MT_CAP_DRAGON / 8; + aspiration_fail1 = MT_CAP_DRAGON / 2; + } + else { + aspiration_hwdth = MT_CAP_DRAGON / 4; + aspiration_fail1 = ( MT_CAP_DRAGON * 3 ) / 4; + } + + switch ( nfail_low ) + { + case 0: root_alpha_old -= aspiration_hwdth; break; + case 1: root_alpha_old -= aspiration_fail1 - aspiration_hwdth; break; + case 2: root_alpha_old = -score_bound; break; + default: + out_error( "Error at set_root_alpha!" ); + exit(1); + } + if ( root_alpha_old < -score_max_eval ) { root_alpha_old = -score_bound; } + + return root_alpha_old; +} + + +static const char * +str_fail_high( int turn, int nfail_high ) +{ + const char *str; + + if ( time_max_limit != time_limit ) + { + if ( nfail_high == 1 ) { str = turn ? "-1" : "+1"; } + else { str = turn ? "-4" : "+4"; } + } + else { + if ( nfail_high == 1 ) { str = turn ? "-2" : "+2"; } + else { str = turn ? "-6" : "+6"; } + } + return str; +} + + +static int +is_answer_right( unsigned int move ) +{ + const char *str_anser; + const char *str_move; + int ianser, iret; + + iret = 0; + str_move = str_CSA_move( move ); + + for ( ianser = 0; ianser < MAX_ANSWER; ianser++ ) + { + str_anser = &( record_problems.info.str_move[ianser][0] ); + if ( str_anser[0] == '\0' ) { break; } + if ( ! strcmp( str_anser+1, str_move ) ) + { + iret = 1; + break; + } + } + + return iret; +} diff --git a/lan.txt b/lan.txt new file mode 100644 index 0000000..680470d --- /dev/null +++ b/lan.txt @@ -0,0 +1,30 @@ +# 'book narrow' allows Bonanza to play only famous opening-lines. +# Use this only when Bonanza plays a small number of games. +book wide +# book narrow + +# Set the time control to 15 minutes - 0 second, limit of depth to 18. +limit time 15 0 18 + +# Set the time margin to 200 miliseconds. +time response 200 + +# In the case of this example, Bonanza uses two threads to search the +# best move. +tlp num 2 + +# Do pondering. +ponder on + +# Set the size of the transposition table to 192 MBytes. +hash 22 + +# Set the resign threashold to 65535 if you do not want Bonanza resigns. +resign 1500 +# resign 65535 + +# connect "hostname" "port #" "login id" "login password" "num games" +connect wdoor.c.u-tokyo.ac.jp 4081 YourID floodgate-900-0,Something 999 + +# quit Bonanza. +quit diff --git a/learn1.c b/learn1.c new file mode 100644 index 0000000..a52ed68 --- /dev/null +++ b/learn1.c @@ -0,0 +1,1074 @@ +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +# include +#else +# include +#endif +#include "shogi.h" + +#if ! defined(MINIMUM) + +# define SEARCH_DEPTH 2 +# define NUM_RESULT 8 +# define MAX_RECORD_LENGTH 1024 +# define DOT_INTERVAL 10 +# define MAX_BOOK_PLY 64 +# define NumBookCluster 256 +# define NumBookEntry 0x1000 +# define SIZE_PV_BUFFER 0x100000 +# define MOVE_VOID 0x80000000U +# define Move2S(move) (unsigned short)((move) & 0x7fffU) + + +typedef struct { + struct { uint64_t a,b; } cluster[NumBookCluster]; +} book_entry_t; + +typedef struct { + + /* input */ + tree_t *ptree; + book_entry_t *pbook_entry; + FILE *pf_tmp; + record_t *precord; + unsigned int max_games, id; + int nworker; + + /* output */ + uint64_t result[ NUM_RESULT ]; + uint64_t num_moves_counted, num_moves, result_norm, num_nodes; + double target, target_out_window; + unsigned int illegal_moves, max_pos_buf; + int info; + + /* work area */ + unsigned int record_moves[ MAX_RECORD_LENGTH ]; + unsigned int amove_legal[ MAX_LEGAL_MOVES ]; + unsigned int record_length, pos_buf; + unsigned short buf[ SIZE_PV_BUFFER ]; + int root_turn; + +} parse1_data_t; + +typedef struct { + + /* input */ + tree_t *ptree; + FILE *pf_tmp; + unsigned int id; + int nworker; + + /* output */ + param_t param; + uint64_t num_moves_counted; + double target; + int info; + + /* work area */ + unsigned short buf[ SIZE_PV_BUFFER ]; + unsigned int pv[ PLY_MAX + 2 ]; + +} parse2_data_t; + +# if defined(_WIN32) +static unsigned int __stdcall parse1_worker( void *arg ); +static unsigned int __stdcall parse2_worker( void *arg ); +# else +static void *parse1_worker( void *arg ); +static void *parse2_worker( void *arg ); +#endif +static void ini_book( book_entry_t *pbook_entry ); +static void make_pv( parse1_data_t *pdata, unsigned int record_move ); +static int read_game( parse1_data_t *pdata ); +static int read_buf( unsigned short *buf, FILE *pf ); +static int calc_deriv( parse2_data_t *pdata, int pos_buf, int turn ); +static int learn_parse1( tree_t * restrict ptree, book_entry_t *pbook_entry, + FILE *pf_tmp, record_t *precord, + unsigned int max_games, double *ptarget_out_window, + double *pobj_norm, int tlp1 ); +static int learn_parse2( tree_t * restrict ptree, FILE *pf_tmp, + int nsteps, double target_out_window, + double obj_norm, int tlp2 ); +static int book_probe_learn( const tree_t * restrict ptree, + book_entry_t *pbook_entry, int ply, + unsigned int move ); +static int rep_check_learn( tree_t * restrict ptree, int ply ); +static unsigned int s2move( const tree_t * restrict ptree, unsigned int move, + int tt ); +static double func( double x ); +static double dfunc( double x ); + +int +learn( tree_t * restrict ptree, int is_ini, int nsteps, unsigned int max_games, + int max_iterations, int nworker1, int nworker2 ) +{ + record_t record; + book_entry_t *pbook_entry; + FILE *pf_tmp; + double target_out_window, obj_norm; + int iret, niterations; + + pbook_entry = memory_alloc( sizeof(book_entry_t) * NumBookEntry ); + if ( pbook_entry == NULL ) { return -2; } + + if ( is_ini ) + { + fill_param_zero(); + fmg_misc = fmg_cap = fmg_drop = fmg_mt = 0; + fmg_misc_king = fmg_cap_king = 0; + } + else { + fmg_misc = FMG_MISC; + fmg_cap = FMG_CAP; + fmg_drop = FMG_DROP; + fmg_mt = FMG_MT; + fmg_misc_king = FMG_MISC_KING; + fmg_cap_king = FMG_CAP_KING; + } + + game_status |= flag_learning; + root_alpha = - ( score_max_eval + 1 ); + root_beta = + ( score_max_eval + 1 ); + root_abort = 0; + iret = 1; + + for ( niterations = 1; niterations <= max_iterations; niterations++ ) + { + Out( "\n Iteration %03d\n", niterations ); + + iret = record_open( &record, "records.csa", mode_read, NULL, NULL ); + if ( iret < 0 ) { break; } + + pf_tmp = file_open( "tmp.bin", "wb" ); + if ( pf_tmp == NULL ) + { + record_close( &record ); + iret = -2; + break; + } + + iret = learn_parse1( ptree, pbook_entry, pf_tmp, &record, max_games, + &target_out_window, &obj_norm, nworker1 ); + + if ( iret < 0 ) + { + record_close( &record ); + file_close( pf_tmp ); + break; + } + + iret = file_close( pf_tmp ); + if ( iret < 0 ) + { + record_close( &record ); + break; + } + + pf_tmp = file_open( "tmp.bin", "rb" ); + if ( pf_tmp == NULL ) + { + record_close( &record ); + iret = -2; + break; + } + + iret = learn_parse2( ptree, pf_tmp, nsteps, target_out_window, obj_norm, + nworker2 ); + if ( iret < 0 ) + { + file_close( pf_tmp ); + record_close( &record ); + break; + } + + iret = file_close( pf_tmp ); + if ( iret < 0 ) + { + record_close( &record ); + break; + } + + iret = record_close( &record ); + if ( iret < 0 ) { break; } + + if ( ! ( game_status & flag_learning ) ) + { + out_warning( "flag_learning is not set." ); + } + } + + memory_free( pbook_entry ); + game_status &= ~flag_learning; + + return iret; +} + + +static int +learn_parse1( tree_t * restrict ptree, book_entry_t *pbook_entry, FILE *pf_tmp, + record_t *precord, unsigned int max_games, + double *ptarget_out_window, double *pobj_norm, int nworker ) +{ + parse1_data_t *pdata[ TLP_MAX_THREADS ]; + int i, id; + + ini_book( pbook_entry ); + + for ( id = 0; id < nworker; id++ ) + { + pdata[id] = memory_alloc( sizeof(parse1_data_t) ); + if ( pdata[id] == NULL ) { return -1; } + + pdata[id]->ptree = NULL; + pdata[id]->pbook_entry = pbook_entry; + pdata[id]->pf_tmp = pf_tmp; + pdata[id]->precord = precord; + pdata[id]->max_games = max_games; + pdata[id]->id = id; + pdata[id]->nworker = nworker; + } + +#if defined(TLP) + for ( id = 1; id < nworker; id++ ) + { +# if defined(_WIN32) + pdata[id]->ptree = tlp_atree_work + id; + if ( ! _beginthreadex( 0, 0, parse1_worker, pdata[id], 0, 0 ) ) + { + str_error = "_beginthreadex() failed."; + return -1; + } +# else + pthread_t pt; + + pdata[id]->ptree = tlp_atree_work + id; + if ( pthread_create( &pt, &pthread_attr, parse1_worker, pdata[id] ) ) + { + str_error = "pthread_create() failed."; + return -1; + } +# endif + } +#endif /* TLP */ + + pdata[0]->ptree = ptree; + parse1_worker( pdata[0] ); + +#if defined(TLP) + while ( tlp_num ) { tlp_yield(); } +#endif + + for ( id = 0; id < nworker; id++ ) + { + if ( pdata[id]->info < 0 ) { return -1; } + } + + for ( id = 1; id < nworker; id++ ) + { + for ( i = 0; i < NUM_RESULT; i++ ) + { + pdata[0]->result[i] += pdata[id]->result[i]; + } + pdata[0]->num_moves_counted += pdata[id]->num_moves_counted; + pdata[0]->num_moves += pdata[id]->num_moves; + pdata[0]->num_nodes += pdata[id]->num_nodes; + pdata[0]->result_norm += pdata[id]->result_norm; + pdata[0]->target += pdata[id]->target; + pdata[0]->target_out_window += pdata[id]->target_out_window; + pdata[0]->illegal_moves += pdata[id]->illegal_moves; + if ( pdata[0]->max_pos_buf < pdata[id]->max_pos_buf ) + { + pdata[0]->max_pos_buf = pdata[id]->max_pos_buf; + } + } + if ( pdata[0]->result_norm == 0 ) { pdata[0]->result_norm = 1; } + if ( pdata[0]->num_moves == 0 ) { pdata[0]->num_moves = 1; } + *ptarget_out_window = pdata[0]->target_out_window; + *pobj_norm = (double)pdata[0]->num_moves; + + { + double dtemp; + int misc, drop, cap, mt, misc_king, cap_king; + + Out( " done\n" ); + Out( " Number of Games : %u\n", precord->games ); + Out( " Total Moves : %"PRIu64"\n",pdata[0]->num_moves ); + Out( " Moves Counted : %"PRIu64"\n",pdata[0]->num_moves_counted); + Out( " Illegal Moves : %u\n", pdata[0]->illegal_moves ); + Out( " Nodes Searched : %"PRIu64"\n",pdata[0]->num_nodes ); + Out( " Max pos_buf : %x\n", pdata[0]->max_pos_buf ); + Out( " Move Prediction :" ); + for ( i = 0, dtemp = 0.0; i < NUM_RESULT; i++ ) + { + dtemp += (double)pdata[0]->result[i] * 100.0; + Out( " %4.1f%%", dtemp / (double)pdata[0]->result_norm ); + } + Out( "\n" ); + + pdata[0]->target /= *pobj_norm; + dtemp = *ptarget_out_window / *pobj_norm; + Out( " Target : %f (%f)\n", pdata[0]->target, dtemp ); + + misc = fmg_misc / 2; + drop = fmg_drop / 2; + cap = fmg_cap / 2; + mt = fmg_mt / 2; + misc_king = fmg_misc_king / 2; + cap_king = fmg_cap_king / 2; + Out( " Futility : misc=%d drop=%d cap=%d mt=%d misc(k)=%d " + "cap(k)=%d\n", misc, drop, cap, mt, misc_king, cap_king ); + } + + for ( id = 0; id < nworker; id++ ) { memory_free( pdata[id] ); } + + return 1; +} + + +# if defined(_MSC_VER) +static unsigned int __stdcall parse1_worker( void *arg ) +# else +static void *parse1_worker( void *arg ) +#endif +{ + parse1_data_t *pdata; + tree_t *ptree; + unsigned int record_move; + int i, imove, iret; + + iret = 0; + pdata = (parse1_data_t *)arg; + ptree = pdata->ptree; + +#if defined(TLP) + if ( pdata->nworker > 1 ) + { + lock( &tlp_lock ); + tlp_num += 1; + if ( pdata->id ) { Out( "hi from thread #%d\n", pdata->id ); } + unlock( &tlp_lock ); + while ( tlp_num < pdata->nworker ) { tlp_yield(); } + } +#endif + + for ( i = 0; i < NUM_RESULT; i++ ) { pdata->result[i] = 0; } + pdata->num_moves_counted = 0; + pdata->num_moves = 0; + pdata->num_nodes = 0; + pdata->max_pos_buf = 0; + pdata->result_norm = 0; + pdata->record_length = 0; + pdata->illegal_moves = 0; + pdata->info = 0; + pdata->target = 0.0; + pdata->target_out_window = 0.0; + + for ( ;; ) { + /* make results */ + pdata->pos_buf = 2U; + for ( imove = 0; imove < (int)pdata->record_length; imove++ ) + { + record_move = pdata->record_moves[imove]; + + pdata->buf[ pdata->pos_buf++ ] = Move2S(record_move); + + if ( record_move & MOVE_VOID ) { record_move &= ~MOVE_VOID; } + else { make_pv( pdata, record_move ); } + + pdata->buf[ pdata->pos_buf++ ] = 0; + + MakeMove( pdata->root_turn, record_move, 1 ); + pdata->root_turn = Flip( pdata->root_turn ); + ptree->move_last[1] = ptree->move_last[0]; + ptree->nsuc_check[0] = 0; + ptree->nsuc_check[1] + = (unsigned char)( InCheck( pdata->root_turn ) ? 1U : 0 ); + } + +#if defined(TLP) + if ( pdata->nworker > 1 ) { lock( &tlp_lock ); } +#endif + + /* save results */ + if ( pdata->record_length ) + { + if ( pdata->pos_buf > pdata->max_pos_buf ) + { + pdata->max_pos_buf = pdata->pos_buf; + } + pdata->buf[0] = (unsigned short)( pdata->pos_buf / 0x10000U ); + pdata->buf[1] = (unsigned short)( pdata->pos_buf % 0x10000U ); + + if ( fwrite( pdata->buf, sizeof(unsigned short), pdata->pos_buf, + pdata->pf_tmp ) != pdata->pos_buf ) + { + str_error = str_io_error; + iret = -2; + } + } + + /* read next game */ + while ( iret >= 0 ) { + iret = read_game( pdata ); + if ( iret == 1 ) { break; } /* end of record */ + if ( pdata->record_length ) { break; } /* read a record */ + } + +#if defined(TLP) + if ( pdata->nworker > 1 ) { unlock( &tlp_lock ); } +#endif + + if ( iret < 0 ) { break; } + if ( iret == 1 ) { break; } + } + +#if defined(TLP) + if ( pdata->nworker > 1 ) + { + lock( &tlp_lock ); + tlp_num -= 1; + unlock( &tlp_lock ); + } +#endif + + pdata->info = iret; + return 0; +} + + +static void +make_pv( parse1_data_t *pdata, unsigned int record_move ) +{ + double func_value; + tree_t *ptree; + unsigned int *pmove; + unsigned int move, pos_buf; + int i, imove, record_value, nth, nc, nmove_legal; + int value, alpha, beta, depth, ply, tt; + + record_value = INT_MIN; + nc = 0; + nth = 0; + depth = PLY_INC * SEARCH_DEPTH + PLY_INC / 2; + tt = Flip(pdata->root_turn); + pos_buf = pdata->pos_buf; + ptree = pdata->ptree; + ptree->node_searched = 0; +#if defined(TLP) + ptree->tlp_abort = 0; +#endif + for ( ply = 0; ply < PLY_MAX; ply++ ) + { + ptree->amove_killer[ply].no1 = ptree->amove_killer[ply].no2 = 0U; + ptree->killers[ply].no1 = ptree->killers[ply].no2 = 0U; + } + for ( i = 0; i < HIST_SIZE; i++ ) + { + ptree->hist_good[i] /= 256U; + ptree->hist_tried[i] /= 256U; + } + + pmove = GenCaptures ( pdata->root_turn, pdata->amove_legal ); + pmove = GenNoCaptures ( pdata->root_turn, pmove ); + pmove = GenDrop ( pdata->root_turn, pmove ); + pmove = GenCapNoProEx2 ( pdata->root_turn, pmove ); + pmove = GenNoCapNoProEx2( pdata->root_turn, pmove ); + nmove_legal = (int)( pmove - pdata->amove_legal ); + + for ( i = 0; pdata->amove_legal[i] != record_move; i++ ); + move = pdata->amove_legal[0]; + pdata->amove_legal[0] = pdata->amove_legal[i]; + pdata->amove_legal[i] = move; + + for ( imove = 0; imove < nmove_legal; imove++ ) { + move = pdata->amove_legal[imove]; + if ( imove ) + { + alpha = record_value - FV_WINDOW; + beta = record_value + FV_WINDOW; + if ( alpha < root_alpha ) { alpha = root_alpha; } + if ( beta > root_beta ) { beta = root_beta; } + } + else { + alpha = root_alpha; + beta = root_beta; + } + + MakeMove( pdata->root_turn, move, 1 ); + if ( InCheck(pdata->root_turn) ) + { + UnMakeMove( pdata->root_turn, move, 1 ); + continue; + } + + if ( InCheck(tt) ) + { + ptree->nsuc_check[2] + = (unsigned char)( ptree->nsuc_check[0] + 1U ); + } else { ptree->nsuc_check[2] = 0; } + + ptree->current_move[1] = move; + ptree->pv[1].type = no_rep; + + value = -search( ptree, -beta, -alpha, tt, depth, 2, + node_do_mate | node_do_null | node_do_futile + | node_do_recap ); + + UnMakeMove( pdata->root_turn, move, 1 ); + + if ( abs(value) > score_mate1ply ) + { + out_warning( "value is larger than mate1ply!" ); + } + + if ( imove ) + { + func_value = func( value - record_value ); + pdata->target += func_value; + pdata->num_moves += 1U; + if ( alpha < value && value < beta ) + { + nc += 1; + } + else { pdata->target_out_window += func_value; } + if ( value >= record_value ) { nth += 1; } + } + else if ( alpha < value && value < beta ) + { + nth += 1; + record_value = value; + } + else { break; } /* record move failed high or low. */ + + if ( alpha < value && value < beta ) + { + pdata->buf[ pos_buf++ ] = Move2S(move); + for ( ply = 2; ply <= ptree->pv[1].length; ply++ ) + { + pdata->buf[ pos_buf++ ] = Move2S(ptree->pv[1].a[ply]); + } + pdata->buf[ pos_buf - 1 ] |= 0x8000U; + } + } + + if ( nth - 1 >= 0 ) + { + pdata->result_norm += 1; + if ( nth-1 < NUM_RESULT ) { pdata->result[nth-1] += 1; } + } + + if ( nc ) + { + pdata->pos_buf = pos_buf; + pdata->num_moves_counted += nc; + } + pdata->num_nodes += ptree->node_searched; +} + + +static int +read_game( parse1_data_t *pdata ) +{ + tree_t *ptree; + tree_t tree; + unsigned int record_move; + int istatus, iret, imove; + + ptree = & tree; + istatus = 0; + if ( pdata->precord->games == pdata->max_games ) { return 1; } + + if ( pdata->precord->games == 0 ) { Out( " Parse 1 " ); } + + if ( ! ( (pdata->precord->games+1) % DOT_INTERVAL ) ) + { + if ( ! ( (pdata->precord->games+1) % ( DOT_INTERVAL * 10 ) ) ) + { + Out( "o" ); + if ( ! ( (pdata->precord->games+1) % ( DOT_INTERVAL * 50 ) ) ) + { + Out( "%7d\n ", pdata->precord->games+1 ); + } + } + else { Out( "." ); } + } + + for ( imove = 0; imove < MAX_RECORD_LENGTH; imove++ ) + { + istatus = in_CSA( ptree, pdata->precord, &record_move, + flag_nomake_move | flag_detect_hang | flag_nofmargin ); + if ( istatus < 0 ) + { + pdata->illegal_moves += 1; + break; + } + if ( istatus >= record_eof ) { break; } + + if ( ! imove + && ( root_turn != min_posi_no_handicap.turn_to_move + || HAND_B != min_posi_no_handicap.hand_black + || HAND_W != min_posi_no_handicap.hand_white + || memcmp( BOARD, min_posi_no_handicap.asquare, nsquare ) ) ) + { + break; + } + + if ( ! imove ) + { + *(pdata->ptree) = *ptree; + pdata->root_turn = root_turn; + } + + if ( rep_check_learn( ptree, 1 ) == four_fold_rep + || ( pdata->precord->moves < MAX_BOOK_PLY + && book_probe_learn( ptree, pdata->pbook_entry, + pdata->precord->moves, record_move ) ) ) + { + pdata->record_moves[ imove ] = record_move | MOVE_VOID; + } + else { pdata->record_moves[ imove ] = record_move; } + + iret = make_move_root( ptree, record_move, flag_rejections ); + if ( iret < 0 ) { return iret; } + } + + if ( istatus != record_next && istatus != record_eof ) + { + istatus = record_wind( pdata->precord ); + if ( istatus < 0 ) { return istatus; } + } + + if ( ! imove && istatus == record_eof ) { return 1; } + pdata->record_length = imove; + + return 0; +} + + +static int +learn_parse2( tree_t * restrict ptree, FILE *pf_tmp, int nsteps, + double target_out_window, double obj_norm, int nworker ) +{ + parse2_data_t *pdata[ TLP_MAX_THREADS ]; + int istep, id; + + for ( id = 0; id < nworker; id++ ) + { + pdata[id] = memory_alloc( sizeof(parse2_data_t) ); + if ( pdata[id] == NULL ) { return -1; } + + pdata[id]->ptree = NULL; + pdata[id]->pf_tmp = pf_tmp; + pdata[id]->id = id; + pdata[id]->nworker = nworker; + } + + Out( " Parse 2\n" ); + + istep = 0; + for ( ;; ) { + +#if defined(TLP) + for ( id = 1; id < nworker; id++ ) + { +# if defined(_WIN32) + pdata[id]->ptree = tlp_atree_work + id; + if ( ! _beginthreadex( 0, 0, parse2_worker, pdata[id], 0, 0 ) ) + { + str_error = "_beginthreadex() failed."; + return -1; + } +# else + pthread_t pt; + + pdata[id]->ptree = tlp_atree_work + id; + if ( pthread_create( &pt, &pthread_attr, parse2_worker, pdata[id] ) ) + { + str_error = "pthread_create() failed."; + return -1; + } +# endif + } +#endif /* TLP */ + + pdata[0]->ptree = ptree; + parse2_worker( pdata[0] ); + +#if defined(TLP) + while ( tlp_num ) { tlp_yield(); } +#endif + + for ( id = 0; id < nworker; id++ ) + { + if ( pdata[id]->info < 0 ) { return -1; } + } + + for ( id = 1; id < nworker; id++ ) + { + add_param( &pdata[0]->param, &pdata[id]->param ); + pdata[0]->num_moves_counted += pdata[id]->num_moves_counted; + pdata[0]->target += pdata[id]->target; + } + + if ( ! istep ) + { + double target, penalty, objective_function; + + penalty = calc_penalty() / obj_norm; + target = ( pdata[0]->target + target_out_window ) / obj_norm; + objective_function = target + penalty; + Out( " Moves Counted : %d\n", pdata[0]->num_moves_counted ); + Out( " Objective Func. : %f %f %f\n", + objective_function, target, penalty ); + Out( " Steps " ); + } + + param_sym( &pdata[0]->param ); + + renovate_param( &pdata[0]->param ); + istep += 1; + if ( istep < nsteps ) { Out( "." ); } + else { + Out( ". done\n\n" ); + break; + } + rewind( pf_tmp ); + } + for ( id = 0; id < nworker; id++ ) { memory_free( pdata[id] ); } + + return out_param(); +} + + +# if defined(_MSC_VER) +static unsigned int __stdcall parse2_worker( void *arg ) +# else +static void *parse2_worker( void *arg ) +#endif +{ + parse2_data_t *pdata; + tree_t *ptree; + unsigned int record_move; + int iret, imove, nbuf, pos_buf, turn; + + iret = 0; + pdata = (parse2_data_t *)arg; + ptree = pdata->ptree; + +#if defined(TLP) + if ( pdata->nworker > 1 ) + { + lock( &tlp_lock ); + tlp_num += 1; + unlock( &tlp_lock ); + while ( tlp_num < pdata->nworker ) { tlp_yield(); } + } +#endif + + pdata->num_moves_counted = 0; + pdata->info = 0; + pdata->target = 0.0; + ini_param( &pdata->param ); + + for ( ;; ) { +#if defined(TLP) + if ( pdata->nworker > 1 ) { lock( &tlp_lock ); } +#endif + iret = read_buf( pdata->buf, pdata->pf_tmp ); +#if defined(TLP) + if ( pdata->nworker > 1 ) { unlock( &tlp_lock ); } +#endif + + if ( iret <= 0 ) { break; } /* 0: eof, -2: error */ + nbuf = iret; + + iret = ini_game( ptree, &min_posi_no_handicap, flag_nofmargin, + NULL, NULL ); + if ( iret < 0 ) { break; } + + turn = black; + pos_buf = 2; + for ( imove = 0; pos_buf < nbuf; imove++ ) + { + record_move = s2move( ptree, pdata->buf[pos_buf++], turn ); + + if ( pdata->buf[pos_buf] ) + { + pos_buf = calc_deriv( pdata, pos_buf, turn ); + } + pos_buf += 1; + + MakeMove( turn, record_move, 1 ); + turn = Flip( turn ); + ptree->move_last[1] = ptree->move_last[0]; + ptree->nsuc_check[0] = 0; + ptree->nsuc_check[1] + = (unsigned char)( InCheck( turn ) ? 1U : 0 ); + } + } + +#if defined(TLP) + if ( pdata->nworker > 1 ) + { + lock( &tlp_lock ); + tlp_num -= 1; + unlock( &tlp_lock ); + } +#endif + + pdata->info = iret; + return 0; +} + + +static int +read_buf( unsigned short *buf, FILE *pf ) +{ + size_t size; + + size = fread( buf, sizeof(unsigned short), 2, pf ); + if ( ! size && feof( pf ) ) { return 0; } + if ( size != 2 ) + { + str_error = str_io_error; + return -2; + } + + size = (size_t)buf[1] + (size_t)buf[0] * 0x10000 - 2; + if ( fread( buf+2, sizeof(unsigned short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + return (int)size; +} + + +static int +calc_deriv( parse2_data_t *pdata, int pos0, int turn0 ) +{ + double target, dT, sum_dT; + tree_t * restrict ptree; + const unsigned short *buf; + unsigned int nc; + int ply, turn, pv_length, pos, record_value, value; + + ptree = pdata->ptree; + buf = pdata->buf; + nc = 0; + turn = turn0; + pos = pos0; + target = 0.0; + sum_dT = 0.0; + + ply = 1; + for ( ;; ) { + pdata->pv[ply] = s2move( ptree, buf[ pos+ply-1 ] & 0x7fffU, turn ); + MakeMove( turn, pdata->pv[ply], ply ); + turn = Flip( turn ); + if ( buf[ pos+ply-1 ] & 0x8000U ) { break; } + ply += 1; + } + pv_length = ply; + + record_value = evaluate( ptree, ply+1, turn ); + if ( turn != turn0 ) { record_value = -record_value; } + + for ( ;; ) { + turn = Flip( turn ); + UnMakeMove( turn, pdata->pv[ply], ply ); + if ( ply == 1 ) { break; } + ply -= 1; + } + pos += pv_length; + + while ( buf[ pos ] ) { + ply = 1; + for ( ;; ) { + pdata->pv[ply] = s2move( ptree, buf[ pos+ply-1 ] & 0x7fffU, turn ); + MakeMove( turn, pdata->pv[ply], ply ); + turn = Flip( turn ); + if ( buf[ pos+ply-1 ] & 0x8000U ) { break; } + ply += 1; + } + pv_length = ply; + + value = evaluate( ptree, ply+1, turn ); + if ( turn != turn0 ) { value = -value; } + target += func( value - record_value ); + + dT = dfunc( value - record_value ); + if ( turn0 ) { dT = -dT; } + sum_dT += dT; + inc_param( ptree, &pdata->param, -dT ); + + for ( ;; ) { + turn = Flip( turn ); + UnMakeMove( turn, pdata->pv[ply], ply ); + if ( ply == 1 ) { break; } + ply -= 1; + } + + pos += pv_length; + nc += 1; + } + + ply = 1; + for ( ;; ) { + pdata->pv[ply] = s2move( ptree, buf[ pos0+ply-1 ] & 0x7fffU, turn ); + MakeMove( turn, pdata->pv[ply], ply ); + turn = Flip( turn ); + if ( buf[ pos0+ply-1 ] & 0x8000U ) { break; } + ply += 1; + } + + inc_param( ptree, &pdata->param, sum_dT ); + + for ( ;; ) { + turn = Flip( turn ); + UnMakeMove( turn, pdata->pv[ply], ply ); + if ( ply == 1 ) { break; } + ply -= 1; + } + + pdata->num_moves_counted += nc; + pdata->target += target; + + return pos; +} + + +static double +func( double x ) +{ + const double delta = (double)FV_WINDOW / 7.0; + double d; + + if ( x < -FV_WINDOW ) { x = -FV_WINDOW; } + else if ( x > FV_WINDOW ) { x = FV_WINDOW; } + d = 1.0 / ( 1.0 + exp(-x/delta) ); + + return d; +} + + +static double +dfunc( double x ) +{ + const double delta = (double)FV_WINDOW / 7.0; + double dd, dn, dtemp, dret; + + if ( x <= -FV_WINDOW ) { dret = 0.0; } + else if ( x >= FV_WINDOW ) { dret = 0.0; } + else { + dn = exp( - x / delta ); + dtemp = dn + 1.0; + dd = delta * dtemp * dtemp; + dret = dn / dd; + } + + return dret; +} + + +static unsigned int +s2move( const tree_t * restrict ptree, unsigned int move, int tt ) +{ + int from, to; + + from = I2From(move); + if ( from < nsquare ) + { + to = I2To(move); + move |= tt ? (Piece2Move(-BOARD[from])|Cap2Move( BOARD[to])) + : (Piece2Move( BOARD[from])|Cap2Move(-BOARD[to])); + } + return move; +} + + +static void +ini_book( book_entry_t *pbook_entry ) +{ + int i, j; + + for ( i = 0; i < NumBookEntry; i++ ) + for ( j = 0; j < NumBookCluster; j++ ) { + pbook_entry[i].cluster[j].a = (uint64_t)0; + pbook_entry[i].cluster[j].b = (uint64_t)0x1ffU << 41; + } +} + + +/* +a: key 64 0 + +b: ply 9 41 + move 19 22 + turn 1 21 + hand 21 0 +*/ +static int +book_probe_learn( const tree_t * restrict ptree, + book_entry_t *pbook_entry, + int ply, unsigned int move ) +{ + book_entry_t *p; + int i, j; + + move &= 0x7ffffU; + p = pbook_entry + + ((unsigned int)HASH_KEY & (unsigned int)(NumBookEntry-1)); + + for ( i = 0; i < NumBookCluster; i++ ) + if ( p->cluster[i].a == HASH_KEY + && ((unsigned int)p->cluster[i].b & 0x1fffffU) == HAND_B + && ( (((unsigned int)p->cluster[i].b>>21) & 0x1U) + == (unsigned int)root_turn ) + && ((unsigned int)(p->cluster[i].b>>22) & 0x7ffffU) == move ) { + return 1; + } + + for ( i = 0; i < NumBookCluster; i++ ) + if ( ( (unsigned int)( p->cluster[i].b >> 41 ) & 0x1ffU ) + > (unsigned int)ply ) { break; } + + if ( i < NumBookCluster ) { + for ( j = NumBookCluster-1; j > i; j-- ) { + p->cluster[j].a = p->cluster[j-1].a; + p->cluster[j].b = p->cluster[j-1].b; + } + + p->cluster[i].a = HASH_KEY; + p->cluster[i].b + = ( (uint64_t)move<<22 ) | ( (uint64_t)ply << 41 ) + | (uint64_t)( (root_turn<<21) | HAND_B ); + } + + return 0; +} + + +static int +rep_check_learn( tree_t * restrict ptree, int ply ) +{ + int n, i, imin; + + n = root_nrep + ply - 1; + imin = n - REP_MAX_PLY; + if ( imin < 0 ) { imin = 0; } + + for ( i = n-2; i >= imin; i -= 2 ) + if ( ptree->rep_board_list[i] == HASH_KEY + && ptree->rep_hand_list[i] == HAND_B ) { return four_fold_rep; } + + return no_rep; +} + +#endif /* no MINIMUM */ diff --git a/learn2.c b/learn2.c new file mode 100644 index 0000000..109424f --- /dev/null +++ b/learn2.c @@ -0,0 +1,856 @@ +#include +#include +#include +#include +#include +#include "shogi.h" + +#if ! defined(MINIMUM) + +#define GO_THROUGH_ALL_PARAMETERS_BY_FOO \ + for ( i=0; i < nsquare*pos_n; i++ ) { Foo( pc_on_sq[0][i] ) } \ + for ( i=0; i < nsquare*nsquare*kkp_end; i++ ) { Foo( kkp[0][0][i] ) } + + +static void rmt( const double avalue[16], int pc ); +static void rparam( short *pv, float dv ); +static void fv_sym( void ); +static int brand( void ); +static int make_list( const tree_t * restrict ptree, int list0[52], + int list1[52], int anpiece[16], param_t * restrict pd, + float f ); + + +void +ini_param( param_t *p ) +{ + int i; + + p->pawn = p->lance = p->knight = p->silver = 0.0; + p->gold = p->bishop = p->rook = p->pro_pawn = 0.0; + p->pro_lance = p->pro_knight = p->pro_silver = p->horse = 0.0; + p->dragon = 0.0; + +#define Foo(x) p->x = 0; + GO_THROUGH_ALL_PARAMETERS_BY_FOO; +#undef Foo +} + + +void +add_param( param_t *p1, const param_t *p2 ) +{ + int i; + + p1->pawn += p2->pawn; + p1->lance += p2->lance; + p1->knight += p2->knight; + p1->silver += p2->silver; + p1->gold += p2->gold; + p1->bishop += p2->bishop; + p1->rook += p2->rook; + p1->pro_pawn += p2->pro_pawn; + p1->pro_lance += p2->pro_lance; + p1->pro_knight += p2->pro_knight; + p1->pro_silver += p2->pro_silver; + p1->horse += p2->horse; + p1->dragon += p2->dragon; + +#define Foo(x) p1->x += p2->x; + GO_THROUGH_ALL_PARAMETERS_BY_FOO; +#undef Foo +} + + +void +fill_param_zero( void ) +{ + int i; + + p_value[15+pawn] = 100; + p_value[15+lance] = 300; + p_value[15+knight] = 300; + p_value[15+silver] = 400; + p_value[15+gold] = 500; + p_value[15+bishop] = 600; + p_value[15+rook] = 700; + p_value[15+pro_pawn] = 400; + p_value[15+pro_lance] = 400; + p_value[15+pro_knight] = 400; + p_value[15+pro_silver] = 500; + p_value[15+horse] = 800; + p_value[15+dragon] = 1000; + +#define Foo(x) x = 0; + GO_THROUGH_ALL_PARAMETERS_BY_FOO; +#undef Foo + + set_derivative_param(); +} + + +void +param_sym( param_t *p ) +{ + int q, r, il, ir, ir0, jl, jr, k0l, k0r, k1l, k1r; + + for ( k0l = 0; k0l < nsquare; k0l++ ) { + q = k0l / nfile; + r = k0l % nfile; + k0r = q*nfile + nfile-1-r; + if ( k0l > k0r ) { continue; } + + for ( il = 0; il < fe_end; il++ ) { + if ( il < fe_hand_end ) { ir0 = il; } + else { + q = ( il- fe_hand_end ) / nfile; + r = ( il- fe_hand_end ) % nfile; + ir0 = q*nfile + nfile-1-r + fe_hand_end; + } + + for ( jl = 0; jl <= il; jl++ ) { + if ( jl < fe_hand_end ) + { + ir = ir0; + jr = jl; + } + else { + q = ( jl - fe_hand_end ) / nfile; + r = ( jl - fe_hand_end ) % nfile; + jr = q*nfile + nfile-1-r + fe_hand_end; + if ( jr > ir0 ) + { + ir = jr; + jr = ir0; + } + else { ir = ir0; } + } + if ( k0l == k0r && il*(il+1)/2+jl >= ir*(ir+1)/2+jr ) { continue; } + + p->PcPcOnSq(k0l,il,jl) + = p->PcPcOnSq(k0r,ir,jr) + = p->PcPcOnSq(k0l,il,jl) + p->PcPcOnSq(k0r,ir,jr); + } + } + } + + for ( k0l = 0; k0l < nsquare; k0l++ ) { + q = k0l / nfile; + r = k0l % nfile; + k0r = q*nfile + nfile-1-r; + if ( k0l > k0r ) { continue; } + + for ( k1l = 0; k1l < nsquare; k1l++ ) { + q = k1l / nfile; + r = k1l % nfile; + k1r = q*nfile + nfile-1-r; + if ( k0l == k0r && k1l > k1r ) { continue; } + + for ( il = 0; il < kkp_end; il++ ) { + if ( il < kkp_hand_end ) { ir = il; } + else { + q = ( il- kkp_hand_end ) / nfile; + r = ( il- kkp_hand_end ) % nfile; + ir = q*nfile + nfile-1-r + kkp_hand_end; + } + if ( k0l == k0r && k1l == k1r && il >= ir ) { continue; } + + p->kkp[k0l][k1l][il] + = p->kkp[k0r][k1r][ir] + = p->kkp[k0l][k1l][il] + p->kkp[k0r][k1r][ir]; + } + } + } +} + + +static void fv_sym( void ) +{ + int q, r, il, ir, ir0, jl, jr, k0l, k0r, k1l, k1r; + + for ( k0l = 0; k0l < nsquare; k0l++ ) { + q = k0l / nfile; + r = k0l % nfile; + k0r = q*nfile + nfile-1-r; + if ( k0l > k0r ) { continue; } + + for ( il = 0; il < fe_end; il++ ) { + if ( il < fe_hand_end ) { ir0 = il; } + else { + q = ( il- fe_hand_end ) / nfile; + r = ( il- fe_hand_end ) % nfile; + ir0 = q*nfile + nfile-1-r + fe_hand_end; + } + + for ( jl = 0; jl <= il; jl++ ) { + if ( jl < fe_hand_end ) + { + ir = ir0; + jr = jl; + } + else { + q = ( jl - fe_hand_end ) / nfile; + r = ( jl - fe_hand_end ) % nfile; + jr = q*nfile + nfile-1-r + fe_hand_end; + if ( jr > ir0 ) + { + ir = jr; + jr = ir0; + } + else { ir = ir0; } + } + if ( k0l == k0r && il*(il+1)/2+jl >= ir*(ir+1)/2+jr ) { continue; } + + PcPcOnSq(k0l,il,jl) = PcPcOnSq(k0r,ir,jr); + } + } + } + + for ( k0l = 0; k0l < nsquare; k0l++ ) { + q = k0l / nfile; + r = k0l % nfile; + k0r = q*nfile + nfile-1-r; + if ( k0l > k0r ) { continue; } + + for ( k1l = 0; k1l < nsquare; k1l++ ) { + q = k1l / nfile; + r = k1l % nfile; + k1r = q*nfile + nfile-1-r; + if ( k0l == k0r && k1l > k1r ) { continue; } + + for ( il = 0; il < kkp_end; il++ ) { + if ( il < kkp_hand_end ) { ir = il; } + else { + q = ( il- kkp_hand_end ) / nfile; + r = ( il- kkp_hand_end ) % nfile; + ir = q*nfile + nfile-1-r + kkp_hand_end; + } + if ( k0l == k0r && k1l == k1r && il >= ir ) { continue; } + + kkp[k0l][k1l][il] = kkp[k0r][k1r][ir]; + } + } + } +} + + +double +calc_penalty( void ) +{ + uint64_t u64sum; + int i; + + u64sum = 0; + +#define Foo(x) u64sum += (uint64_t)abs((int)x); + GO_THROUGH_ALL_PARAMETERS_BY_FOO; +#undef Foo + + return (double)u64sum * FV_PENALTY; +} + + +void +renovate_param( const param_t *pd ) +{ + double *pv[14], *p; + double v[16]; + unsigned int u32rand, u; + int i, j; + + v[pawn] = pd->pawn; v[lance] = pd->lance; + v[knight] = pd->knight; v[silver] = pd->silver; + v[gold] = pd->gold; v[bishop] = pd->bishop; + v[rook] = pd->rook; v[pro_pawn] = pd->pro_pawn; + v[pro_lance] = pd->pro_lance; v[pro_knight] = pd->pro_knight; + v[pro_silver] = pd->pro_silver; v[horse] = pd->horse; + v[dragon] = pd->dragon; v[king] = FLT_MAX; + + pv[ 0] = v + pawn; pv[ 1] = v + lance; + pv[ 2] = v + knight; pv[ 3] = v + silver; + pv[ 4] = v + gold; pv[ 5] = v + bishop; + pv[ 6] = v + rook; pv[ 7] = v + pro_pawn; + pv[ 8] = v + pro_lance; pv[ 9] = v + pro_knight; + pv[10] = v + pro_silver; pv[11] = v + horse; + pv[12] = v + dragon; pv[13] = v + king; + + /* insertion sort */ + for ( i = 13 - 2; i >= 0; i-- ) + { + p = pv[i]; + for ( j = i+1; *pv[j] < *p; j++ ) { pv[j-1] = pv[j]; } + pv[j-1] = p; + } + + u32rand = rand32(); + u = u32rand % 7U; + u32rand = u32rand / 7U; + p = pv[u+6]; pv[u+6] = pv[12]; pv[12] = p; + for ( i = 5; i > 0; i-- ) + { + u = u32rand % (i+1); + u32rand = u32rand / (i+1); + p = pv[u]; pv[u] = pv[i]; pv[i] = p; + + u = u32rand % (i+1); + u32rand = u32rand / (i+1); + p = pv[u+6]; pv[u+6] = pv[i+6]; pv[i+6] = p; + } + + *pv[ 0] = *pv[ 1] = -2.0; + *pv[ 2] = *pv[ 3] = *pv[ 4] = -1.0; + *pv[ 5] = *pv[ 6] = *pv[ 7] = 0.0; + *pv[ 8] = *pv[ 9] = *pv[10] = 1.0; + *pv[11] = *pv[12] = 2.0; + + rmt( v, pawn ); rmt( v, lance ); rmt( v, knight ); + rmt( v, silver ); rmt( v, gold ); rmt( v, bishop ); + rmt( v, rook ); rmt( v, pro_pawn ); rmt( v, pro_lance ); + rmt( v, pro_knight ); rmt( v, pro_silver ); rmt( v, horse ); + rmt( v, dragon ); + +#define Foo(v) rparam( &v, pd->v ); + GO_THROUGH_ALL_PARAMETERS_BY_FOO; +#undef Foo + + fv_sym(); + set_derivative_param(); + ehash_clear(); +} + + +int +out_param( void ) +{ + size_t size; + FILE *pf; + int apc[17], apv[17]; + int iret, i, j, pc, pv; + + for ( i = 0; i < 17; i++ ) { apc[i] = i; apv[i] = INT_MAX; } + apv[pawn] = p_value_ex[15+pawn]; + apv[lance] = p_value_ex[15+lance]; + apv[knight] = p_value_ex[15+knight]; + apv[silver] = p_value_ex[15+silver]; + apv[gold] = p_value_ex[15+gold]; + apv[bishop] = p_value_ex[15+bishop]; + apv[rook] = p_value_ex[15+rook]; + apv[pro_pawn] = p_value_ex[15+pro_pawn]; + apv[pro_lance] = p_value_ex[15+pro_lance]; + apv[pro_knight] = p_value_ex[15+pro_knight]; + apv[pro_silver] = p_value_ex[15+pro_silver]; + apv[horse] = p_value_ex[15+horse]; + apv[dragon] = p_value_ex[15+dragon]; + + /* insertion sort */ + for ( i = dragon-1; i >= 0; i-- ) + { + pv = apv[i]; pc = apc[i]; + for ( j = i+1; apv[j] < pv; j++ ) + { + apv[j-1] = apv[j]; + apc[j-1] = apc[j]; + } + apv[j-1] = pv; apc[j-1] = pc; + } + + pf = file_open( "param.h_", "w" ); + if ( pf == NULL ) { return -2; } + + for ( i = 0; i < 13; i++ ) + { + fprintf( pf, "#define " ); + switch ( apc[i] ) + { + case pawn: fprintf( pf, "DPawn " ); break; + case lance: fprintf( pf, "DLance " ); break; + case knight: fprintf( pf, "DKnight " ); break; + case silver: fprintf( pf, "DSilver " ); break; + case gold: fprintf( pf, "DGold " ); break; + case bishop: fprintf( pf, "DBishop " ); break; + case rook: fprintf( pf, "DRook " ); break; + case pro_pawn: fprintf( pf, "DProPawn " ); break; + case pro_lance: fprintf( pf, "DProLance " ); break; + case pro_knight: fprintf( pf, "DProKnight" ); break; + case pro_silver: fprintf( pf, "DProSilver" ); break; + case horse: fprintf( pf, "DHorse " ); break; + case dragon: fprintf( pf, "DDragon " ); break; + } + fprintf( pf, " %4d /* %4d */\n", p_value[15+apc[i]], apv[i] ); + } + fprintf( pf, "#define DKing 15000\n\n" ); + + iret = file_close( pf ); + if ( iret < 0 ) { return -2; } + + pf = file_open( "fv.bin", "wb" ); + if ( pf == NULL ) { return -2; } + + size = nsquare * pos_n; + if ( fwrite( pc_on_sq, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + size = nsquare * nsquare * kkp_end; + if ( fwrite( kkp, sizeof(short), size, pf ) != size ) + { + str_error = str_io_error; + return -2; + } + + return file_close( pf ); +} + + +void +inc_param( const tree_t * restrict ptree, param_t * restrict pd, double dinc ) +{ + float f; + int anpiece[16], list0[52], list1[52]; + int nlist, sq_bk, sq_wk, k0, k1, l0, l1, i, j; + + f = (float)( dinc / (double)FV_SCALE ); + nlist = make_list( ptree, list0, list1, anpiece, pd, f ); + sq_bk = SQ_BKING; + sq_wk = Inv( SQ_WKING ); + + pd->pawn += dinc * (double)anpiece[pawn]; + pd->lance += dinc * (double)anpiece[lance]; + pd->knight += dinc * (double)anpiece[knight]; + pd->silver += dinc * (double)anpiece[silver]; + pd->gold += dinc * (double)anpiece[gold]; + pd->bishop += dinc * (double)anpiece[bishop]; + pd->rook += dinc * (double)anpiece[rook]; + pd->pro_pawn += dinc * (double)anpiece[pro_pawn]; + pd->pro_lance += dinc * (double)anpiece[pro_lance]; + pd->pro_knight += dinc * (double)anpiece[pro_knight]; + pd->pro_silver += dinc * (double)anpiece[pro_silver]; + pd->horse += dinc * (double)anpiece[horse]; + pd->dragon += dinc * (double)anpiece[dragon]; + + for ( i = 0; i < nlist; i++ ) + { + k0 = list0[i]; + k1 = list1[i]; + for ( j = 0; j <= i; j++ ) + { + l0 = list0[j]; + l1 = list1[j]; + assert( k0 >= l0 && k1 >= l1 ); + pd->PcPcOnSq( sq_bk, k0, l0 ) += f; + pd->PcPcOnSq( sq_wk, k1, l1 ) -= f; + } + } +} + + +static int +make_list( const tree_t * restrict ptree, int list0[52], int list1[52], + int anpiece[16], param_t * restrict pd, float f ) +{ + bitboard_t bb; + int list2[34]; + int nlist, sq, sq_bk0, sq_bk1, sq_wk0, sq_wk1, n2, i, itemp1, itemp2; + + itemp1 = (int)I2HandPawn(HAND_B); + itemp2 = (int)I2HandPawn(HAND_W); + anpiece[pawn] = itemp1 - itemp2; + + itemp1 = (int)I2HandLance(HAND_B); + itemp2 = (int)I2HandLance(HAND_W); + anpiece[lance] = itemp1 - itemp2; + + itemp1 = (int)I2HandKnight(HAND_B); + itemp2 = (int)I2HandKnight(HAND_W); + anpiece[knight] = itemp1 - itemp2; + + itemp1 = (int)I2HandSilver(HAND_B); + itemp2 = (int)I2HandSilver(HAND_W); + anpiece[silver] = itemp1 - itemp2; + + itemp1 = (int)I2HandGold(HAND_B); + itemp2 = (int)I2HandGold(HAND_W); + anpiece[gold] = itemp1 - itemp2; + + itemp1 = (int)I2HandBishop(HAND_B); + itemp2 = (int)I2HandBishop(HAND_W); + anpiece[bishop] = itemp1 - itemp2; + + itemp1 = (int)I2HandRook(HAND_B); + itemp2 = (int)I2HandRook(HAND_W); + anpiece[rook] = itemp1 - itemp2; + + anpiece[pro_pawn] = anpiece[pro_lance] = anpiece[pro_knight] = 0; + anpiece[pro_silver] = anpiece[horse] = anpiece[dragon] = 0; + nlist = 14; + sq_bk0 = SQ_BKING; + sq_wk0 = SQ_WKING; + sq_bk1 = Inv(SQ_WKING); + sq_wk1 = Inv(SQ_BKING); + + list0[ 0] = f_hand_pawn + I2HandPawn(HAND_B); + list0[ 1] = e_hand_pawn + I2HandPawn(HAND_W); + list0[ 2] = f_hand_lance + I2HandLance(HAND_B); + list0[ 3] = e_hand_lance + I2HandLance(HAND_W); + list0[ 4] = f_hand_knight + I2HandKnight(HAND_B); + list0[ 5] = e_hand_knight + I2HandKnight(HAND_W); + list0[ 6] = f_hand_silver + I2HandSilver(HAND_B); + list0[ 7] = e_hand_silver + I2HandSilver(HAND_W); + list0[ 8] = f_hand_gold + I2HandGold(HAND_B); + list0[ 9] = e_hand_gold + I2HandGold(HAND_W); + list0[10] = f_hand_bishop + I2HandBishop(HAND_B); + list0[11] = e_hand_bishop + I2HandBishop(HAND_W); + list0[12] = f_hand_rook + I2HandRook(HAND_B); + list0[13] = e_hand_rook + I2HandRook(HAND_W); + + list1[ 0] = f_hand_pawn + I2HandPawn(HAND_W); + list1[ 1] = e_hand_pawn + I2HandPawn(HAND_B); + list1[ 2] = f_hand_lance + I2HandLance(HAND_W); + list1[ 3] = e_hand_lance + I2HandLance(HAND_B); + list1[ 4] = f_hand_knight + I2HandKnight(HAND_W); + list1[ 5] = e_hand_knight + I2HandKnight(HAND_B); + list1[ 6] = f_hand_silver + I2HandSilver(HAND_W); + list1[ 7] = e_hand_silver + I2HandSilver(HAND_B); + list1[ 8] = f_hand_gold + I2HandGold(HAND_W); + list1[ 9] = e_hand_gold + I2HandGold(HAND_B); + list1[10] = f_hand_bishop + I2HandBishop(HAND_W); + list1[11] = e_hand_bishop + I2HandBishop(HAND_B); + list1[12] = f_hand_rook + I2HandRook(HAND_W); + list1[13] = e_hand_rook + I2HandRook(HAND_B); + + pd->kkp[sq_bk0][sq_wk0][kkp_hand_pawn +I2HandPawn(HAND_B) ] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_lance +I2HandLance(HAND_B) ] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_knight+I2HandKnight(HAND_B)] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_silver+I2HandSilver(HAND_B)] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_gold +I2HandGold(HAND_B) ] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_bishop+I2HandBishop(HAND_B)] += f; + pd->kkp[sq_bk0][sq_wk0][kkp_hand_rook +I2HandRook(HAND_B) ] += f; + + pd->kkp[sq_bk1][sq_wk1][kkp_hand_pawn +I2HandPawn(HAND_W) ] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_lance +I2HandLance(HAND_W) ] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_knight+I2HandKnight(HAND_W)] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_silver+I2HandSilver(HAND_W)] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_gold +I2HandGold(HAND_W) ] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_bishop+I2HandBishop(HAND_W)] -= f; + pd->kkp[sq_bk1][sq_wk1][kkp_hand_rook +I2HandRook(HAND_W) ] -= f; + + n2 = 0; + bb = BB_BPAWN; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_pawn+sq] += f; + anpiece[pawn] += 1; + list0[nlist] = f_pawn + sq; + list2[n2] = e_pawn + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WPAWN; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_pawn+Inv(sq)] -= f; + anpiece[pawn] -= 1; + list0[nlist] = e_pawn + sq; + list2[n2] = f_pawn + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + n2 = 0; + bb = BB_BLANCE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_lance+sq] += f; + anpiece[lance] += 1; + list0[nlist] = f_lance + sq; + list2[n2] = e_lance + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WLANCE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_lance+Inv(sq)] -= f; + anpiece[lance] -= 1; + list0[nlist] = e_lance + sq; + list2[n2] = f_lance + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BKNIGHT; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_knight+sq] += f; + anpiece[knight] += 1; + list0[nlist] = f_knight + sq; + list2[n2] = e_knight + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WKNIGHT; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_knight+Inv(sq)] -= f; + anpiece[knight] -= 1; + list0[nlist] = e_knight + sq; + list2[n2] = f_knight + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BSILVER; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_silver+sq] += f; + anpiece[silver] += 1; + list0[nlist] = f_silver + sq; + list2[n2] = e_silver + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WSILVER; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_silver+Inv(sq)] -= f; + anpiece[silver] -= 1; + list0[nlist] = e_silver + sq; + list2[n2] = f_silver + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BTGOLD; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_gold+sq] += f; + anpiece[BOARD[sq]] += 1; + list0[nlist] = f_gold + sq; + list2[n2] = e_gold + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WTGOLD; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_gold+Inv(sq)] -= f; + anpiece[-BOARD[sq]] -= 1; + list0[nlist] = e_gold + sq; + list2[n2] = f_gold + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BBISHOP; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_bishop+sq] += f; + anpiece[bishop] += 1; + list0[nlist] = f_bishop + sq; + list2[n2] = e_bishop + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WBISHOP; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_bishop+Inv(sq)] -= f; + anpiece[bishop] -= 1; + list0[nlist] = e_bishop + sq; + list2[n2] = f_bishop + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BHORSE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_horse+sq] += f; + anpiece[horse] += 1; + list0[nlist] = f_horse + sq; + list2[n2] = e_horse + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WHORSE; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_horse+Inv(sq)] -= f; + anpiece[horse] -= 1; + list0[nlist] = e_horse + sq; + list2[n2] = f_horse + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BROOK; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_rook+sq] += f; + anpiece[rook] += 1; + list0[nlist] = f_rook + sq; + list2[n2] = e_rook + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WROOK; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_rook+Inv(sq)] -= f; + anpiece[rook] -= 1; + list0[nlist] = e_rook + sq; + list2[n2] = f_rook + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + + n2 = 0; + bb = BB_BDRAGON; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk0][sq_wk0][kkp_dragon+sq] += f; + anpiece[dragon] += 1; + list0[nlist] = f_dragon + sq; + list2[n2] = e_dragon + Inv(sq); + nlist += 1; + n2 += 1; + } + + bb = BB_WDRAGON; + while ( BBToU(bb) ) { + sq = FirstOne( bb ); + Xor( sq, bb ); + + pd->kkp[sq_bk1][sq_wk1][kkp_dragon+Inv(sq)] -= f; + anpiece[dragon] -= 1; + list0[nlist] = e_dragon + sq; + list2[n2] = f_dragon + Inv(sq); + nlist += 1; + n2 += 1; + } + for ( i = 0; i < n2; i++ ) { list1[nlist-i-1] = list2[i]; } + + assert( nlist <= 52 ); + return nlist; +} + + +static void +rmt( const double avalue[16], int pc ) +{ + int pc_v; + + pc_v = (int)p_value[15+pc] + (int)avalue[pc]; + + if ( 0 < pc_v && pc_v <= SHRT_MAX ) { p_value[15+pc] = (short)pc_v; } + else { + out_warning( "A material value is out of bounce. (%s=%d)\n", + astr_table_piece[pc], pc_v ); + } +} + + +static void +rparam( short *pv, float dv ) +{ + int v, istep; + + istep = brand(); + istep += brand(); + v = *pv; + + if ( v > 0 ) { dv -= (float)FV_PENALTY; } + else if ( v < 0 ) { dv += (float)FV_PENALTY; } + + if ( dv >= 0.0 && v <= SHRT_MAX - istep ) { v += istep; } + else if ( dv <= 0.0 && v >= SHRT_MIN + istep ) { v -= istep; } + else { out_warning( "A fvcoef parameter is out of bounce.\n" ); } + + *pv = (short)v; +} + + +static int +brand( void ) +{ + static unsigned int urand = 0; + static unsigned int uc = 31; + unsigned int uret; + + if ( uc == 31 ) + { + urand = rand32(); + uc = 0; + } + else { uc += 1; } + uret = urand & 1U; + urand >>= 1; + return uret; +} + +#endif /* MINIMUM */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..f84ad15 --- /dev/null +++ b/main.c @@ -0,0 +1,154 @@ +/* + BUG LIST + + - detection of repetitions can be wrong due to collision of hash keys and + limitation of history table size. + + - detection of mates fails if all of pseudo-legal evasions are perpetual + checks. Father more, inferior evasions, such as unpromotion of + bishop, rook, and lance at 8th rank, are not counted for the mate + detection. + + - detection of perpetual checks fails if one of those inferior + evasions makes a position that occurred four times. +*/ +/* + TODO: + - idirec && is_pinned_on_black_king(); + - aifile and airank + - incheck at quies + - max legal moves + - tactical macro + - out_warning( "A node returns a value lower than mate." ); is obsolate. + - do_mate in hash + - pv store to hash + - no threat + - use IsDiscover macro + - change hash_store_pv() + - dek.c is obsolate. + - limit time ? ? num + - hash.bin + - SHARE to all transition table + */ + +#include +#include +#include +#if defined(_WIN32) +# include +#endif +#include "shogi.h" + +static int main_child( tree_t * restrict ptree ); + +int CONV_CDECL +#if defined(CSASHOGI) +main( int argc, char *argv[] ) +#else +main() +#endif +{ + int iret; + tree_t * restrict ptree; + +#if defined(TLP) + ptree = tlp_atree_work; +#else + ptree = &tree; +#endif + +#if defined(CSASHOGI) && defined(_WIN32) + FreeConsole(); + if ( argc != 2 || strcmp( argv[1], "csa_shogi" ) ) + { + MessageBox( NULL, + "The executable image is not intended\x0d" + "as an independent program file.\x0d" + "Execute CSA.EXE instead.", + str_myname, MB_OK | MB_ICONINFORMATION ); + return EXIT_FAILURE; + } +#endif + + if ( ini( ptree ) < 0 ) + { + out_error( "%s", str_error ); + return EXIT_SUCCESS; + } + + for ( ;; ) + { + iret = main_child( ptree ); + if ( iret == -1 ) + { + out_error( "%s", str_error ); + ShutdownClient; + break; + } + else if ( iret == -2 ) + { + out_warning( "%s", str_error ); + ShutdownClient; + continue; + } + else if ( iret == -3 ) { break; } + } + + if ( fin() < 0 ) { out_error( "%s", str_error ); } + + return EXIT_SUCCESS; +} + + +static int +main_child( tree_t * restrict ptree ) +{ + int iret; + +#if defined(DEKUNOBOU) + if ( dek_ngame && ( game_status & mask_game_end ) ) + { + TlpEnd(); + if ( dek_next_game( ptree ) < 0 ) + { + out_error( "%s", str_error ); + return -3; + } + } +#endif + + /* ponder a move */ + ponder_move = 0; + iret = ponder( ptree ); + if ( iret < 0 ) { return iret; } + else if ( game_status & flag_quit ) { return -3; } + + /* move prediction succeeded, pondering finished, + and computer made a move. */ + else if ( iret == 2 ) { return 1; } + + /* move prediction failed, pondering aborted, + and we have opponent's move in input buffer. */ + else if ( ponder_move == MOVE_PONDER_FAILED ) + { + } + + /* pondering is interrupted or ended. + do nothing until we get next input line. */ + else { + TlpEnd(); + show_prompt(); + } + + + iret = next_cmdline( 1 ); + if ( iret < 0 ) { return iret; } + else if ( game_status & flag_quit ) { return -3; } + + + iret = procedure( ptree ); + if ( iret < 0 ) { return iret; } + else if ( game_status & flag_quit ) { return -3; } + + return 1; +} diff --git a/makemove.c b/makemove.c new file mode 100644 index 0000000..83d8c9e --- /dev/null +++ b/makemove.c @@ -0,0 +1,508 @@ +#include +#include +#include "shogi.h" + +#define DropB( PIECE, piece ) Xor( to, BB_B ## PIECE ); \ + HASH_KEY ^= ( b_ ## piece ## _rand )[to]; \ + HAND_B -= flag_hand_ ## piece; \ + BOARD[to] = piece + +#define DropW( PIECE, piece ) Xor( to, BB_W ## PIECE ); \ + HASH_KEY ^= ( w_ ## piece ## _rand )[to]; \ + HAND_W -= flag_hand_ ## piece; \ + BOARD[to] = - piece + +#define CapB( PIECE, piece, pro_piece ) \ + Xor( to, BB_B ## PIECE ); \ + HASH_KEY ^= ( b_ ## pro_piece ## _rand )[to]; \ + HAND_W += flag_hand_ ## piece; \ + MATERIAL -= MT_CAP_ ## PIECE + +#define CapW( PIECE, piece, pro_piece ) \ + Xor( to, BB_W ## PIECE ); \ + HASH_KEY ^= ( w_ ## pro_piece ## _rand )[to]; \ + HAND_B += flag_hand_ ## piece; \ + MATERIAL += MT_CAP_ ## PIECE + +#define NocapProB( PIECE, PRO_PIECE, piece, pro_piece ) \ + Xor( from, BB_B ## PIECE ); \ + Xor( to, BB_B ## PRO_PIECE ); \ + HASH_KEY ^= ( b_ ## pro_piece ## _rand )[to] \ + ^ ( b_ ## piece ## _rand )[from]; \ + MATERIAL += MT_PRO_ ## PIECE; \ + BOARD[to] = pro_piece + +#define NocapProW( PIECE, PRO_PIECE, piece, pro_piece ) \ + Xor( from, BB_W ## PIECE ); \ + Xor( to, BB_W ## PRO_PIECE ); \ + HASH_KEY ^= ( w_ ## pro_piece ## _rand )[to] \ + ^ ( w_ ## piece ## _rand )[from]; \ + MATERIAL -= MT_PRO_ ## PIECE; \ + BOARD[to] = - pro_piece + +#define NocapNoproB( PIECE, piece ) \ + SetClear( BB_B ## PIECE ); \ + HASH_KEY ^= ( b_ ## piece ## _rand )[to] \ + ^ ( b_ ## piece ## _rand )[from]; \ + BOARD[to] = piece + +#define NocapNoproW( PIECE, piece ) \ + SetClear( BB_W ## PIECE ); \ + HASH_KEY ^= ( w_ ## piece ## _rand )[to] \ + ^ ( w_ ## piece ## _rand )[from]; \ + BOARD[to] = - piece + + +void +make_move_b( tree_t * restrict ptree, unsigned int move, int ply ) +{ + const int from = (int)I2From(move); + const int to = (int)I2To(move); + const int nrep = root_nrep + ply - 1; + + assert( UToCap(move) != king ); + assert( move ); + assert( is_move_valid( ptree, move, black ) ); + + ptree->rep_board_list[nrep] = HASH_KEY; + ptree->rep_hand_list[nrep] = HAND_B; + ptree->save_material[ply] = (short)MATERIAL; + ptree->stand_pat[ply+1] = score_bound; + + if ( from >= nsquare ) + { + switch ( From2Drop(from) ) + { + case pawn: Xor( to-nfile, BB_BPAWN_ATK ); + DropB( PAWN, pawn ); break; + case lance: DropB( LANCE, lance ); break; + case knight: DropB( KNIGHT, knight ); break; + case silver: DropB( SILVER, silver ); break; + case gold: DropB( GOLD, gold ); + Xor( to, BB_BTGOLD ); break; + case bishop: DropB( BISHOP, bishop ); + Xor( to, BB_B_BH ); break; + default: assert( From2Drop(from) == rook ); + DropB( ROOK, rook ); + Xor( to, BB_B_RD ); break; + } + Xor( to, BB_BOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + else { + const int ipiece_move = (int)I2PieceMove(move); + const int ipiece_cap = (int)UToCap(move); + const int is_promote = (int)I2IsPromote(move); + bitboard_t bb_set_clear; + + BBOr( bb_set_clear, abb_mask[from], abb_mask[to] ); + SetClear( BB_BOCCUPY ); + BOARD[from] = empty; + + if ( is_promote ) switch( ipiece_move ) + { + case pawn: Xor( to, BB_BPAWN_ATK ); + Xor( to, BB_BTGOLD ); + NocapProB( PAWN, PRO_PAWN, pawn, pro_pawn ); break; + case lance: Xor( to, BB_BTGOLD ); + NocapProB( LANCE, PRO_LANCE, lance, pro_lance ); break; + case knight: Xor( to, BB_BTGOLD ); + NocapProB( KNIGHT, PRO_KNIGHT, knight, pro_knight ); break; + case silver: Xor( to, BB_BTGOLD ); + NocapProB( SILVER, PRO_SILVER, silver, pro_silver ); break; + case bishop: Xor( to, BB_B_HDK ); + SetClear( BB_B_BH ); + NocapProB( BISHOP, HORSE, bishop, horse ); break; + default: assert( ipiece_move == rook ); + Xor( to, BB_B_HDK ); + SetClear( BB_B_RD ); + NocapProB( ROOK, DRAGON, rook, dragon ); break; + } + else switch ( ipiece_move ) + { + case pawn: Xor( to-nfile, BB_BPAWN_ATK ); + Xor( to, BB_BPAWN_ATK ); + NocapNoproB( PAWN, pawn); break; + case lance: NocapNoproB( LANCE, lance); break; + case knight: NocapNoproB( KNIGHT, knight); break; + case silver: NocapNoproB( SILVER, silver); break; + case gold: NocapNoproB( GOLD, gold); + SetClear( BB_BTGOLD ); break; + case bishop: SetClear( BB_B_BH ); + NocapNoproB( BISHOP, bishop); break; + case rook: NocapNoproB( ROOK, rook); + SetClear( BB_B_RD ); break; + case king: HASH_KEY ^= b_king_rand[to] ^ b_king_rand[from]; + SetClear( BB_B_HDK ); + BOARD[to] = king; + SQ_BKING = (char)to; break; + case pro_pawn: NocapNoproB( PRO_PAWN, pro_pawn ); + SetClear( BB_BTGOLD ); break; + case pro_lance: NocapNoproB( PRO_LANCE, pro_lance ); + SetClear( BB_BTGOLD ); break; + case pro_knight: NocapNoproB( PRO_KNIGHT, pro_knight ); + SetClear( BB_BTGOLD ); break; + case pro_silver: NocapNoproB( PRO_SILVER, pro_silver ); + SetClear( BB_BTGOLD ); break; + case horse: NocapNoproB( HORSE, horse ); + SetClear( BB_B_HDK ); + SetClear( BB_B_BH ); break; + default: assert( ipiece_move == dragon ); + NocapNoproB( DRAGON, dragon ); + SetClear( BB_B_HDK ); + SetClear( BB_B_RD ); break; + } + + if ( ipiece_cap ) + { + switch( ipiece_cap ) + { + case pawn: CapW( PAWN, pawn, pawn ); + Xor( to+nfile, BB_WPAWN_ATK ); break; + case lance: CapW( LANCE, lance, lance ); break; + case knight: CapW( KNIGHT, knight, knight ); break; + case silver: CapW( SILVER, silver, silver ); break; + case gold: CapW( GOLD, gold, gold ); + Xor( to, BB_WTGOLD ); break; + case bishop: CapW( BISHOP, bishop, bishop ); + Xor( to, BB_W_BH ); break; + case rook: CapW( ROOK, rook, rook); + Xor( to, BB_W_RD ); break; + case pro_pawn: CapW( PRO_PAWN, pawn, pro_pawn ); + Xor( to, BB_WTGOLD ); break; + case pro_lance: CapW( PRO_LANCE, lance, pro_lance ); + Xor( to, BB_WTGOLD ); break; + case pro_knight: CapW( PRO_KNIGHT, knight, pro_knight ); + Xor( to, BB_WTGOLD ); break; + case pro_silver: CapW( PRO_SILVER, silver, pro_silver ); + Xor( to, BB_WTGOLD ); break; + case horse: CapW( HORSE, bishop, horse ); + Xor( to, BB_W_HDK ); + Xor( to, BB_W_BH ); break; + default: assert( ipiece_cap == dragon ); + CapW( DRAGON, rook, dragon ); + Xor( to, BB_W_HDK ); + Xor( to, BB_W_RD ); break; + } + Xor( to, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + } + else { + SetClearFile( from, to, OCCUPIED_FILE ); + SetClearDiag1( from, to, OCCUPIED_DIAG1 ); + SetClearDiag2( from, to, OCCUPIED_DIAG2 ); + } + } + + assert( exam_bb( ptree ) ); +} + + +void +make_move_w( tree_t * restrict ptree, unsigned int move, int ply ) +{ + const int from = (int)I2From(move); + const int to = (int)I2To(move); + const int nrep = root_nrep + ply - 1; + + assert( UToCap(move) != king ); + assert( move ); + assert( is_move_valid( ptree, move, white ) ); + + ptree->rep_board_list[nrep] = HASH_KEY; + ptree->rep_hand_list[nrep] = HAND_B; + ptree->save_material[ply] = (short)MATERIAL; + ptree->stand_pat[ply+1] = score_bound; + + if ( from >= nsquare ) + { + switch( From2Drop(from) ) + { + case pawn: Xor( to+nfile, BB_WPAWN_ATK ); + DropW( PAWN, pawn ); break; + case lance: DropW( LANCE, lance ); break; + case knight: DropW( KNIGHT, knight ); break; + case silver: DropW( SILVER, silver ); break; + case gold: DropW( GOLD, gold ); + Xor( to, BB_WTGOLD ); break; + case bishop: DropW( BISHOP, bishop ); + Xor( to, BB_W_BH ); break; + default: DropW( ROOK, rook ); + Xor( to, BB_W_RD ); break; + } + Xor( to, BB_WOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + else { + const int ipiece_move = (int)I2PieceMove(move); + const int ipiece_cap = (int)UToCap(move); + const int is_promote = (int)I2IsPromote(move); + bitboard_t bb_set_clear; + + BBOr( bb_set_clear, abb_mask[from], abb_mask[to] ); + SetClear( BB_WOCCUPY ); + BOARD[from] = empty; + + if ( is_promote) switch( ipiece_move ) + { + case pawn: NocapProW( PAWN, PRO_PAWN, pawn, pro_pawn ); + Xor( to, BB_WPAWN_ATK ); + Xor( to, BB_WTGOLD ); break; + case lance: NocapProW( LANCE, PRO_LANCE, lance, pro_lance ); + Xor( to, BB_WTGOLD ); break; + case knight: NocapProW( KNIGHT, PRO_KNIGHT, knight, pro_knight ); + Xor( to, BB_WTGOLD ); break; + case silver: NocapProW( SILVER, PRO_SILVER, silver, pro_silver ); + Xor( to, BB_WTGOLD ); break; + case bishop: NocapProW( BISHOP, HORSE, bishop, horse ); + Xor( to, BB_W_HDK ); + SetClear( BB_W_BH ); break; + default: NocapProW( ROOK, DRAGON, rook, dragon); + Xor( to, BB_W_HDK ); + SetClear( BB_W_RD ); break; + } + else switch ( ipiece_move ) + { + case pawn: NocapNoproW( PAWN, pawn ); + Xor( to+nfile, BB_WPAWN_ATK ); + Xor( to, BB_WPAWN_ATK ); break; + case lance: NocapNoproW( LANCE, lance); break; + case knight: NocapNoproW( KNIGHT, knight); break; + case silver: NocapNoproW( SILVER, silver); break; + case gold: NocapNoproW( GOLD, gold); + SetClear( BB_WTGOLD ); break; + case bishop: NocapNoproW( BISHOP, bishop); + SetClear( BB_W_BH ); break; + case rook: NocapNoproW( ROOK, rook); + SetClear( BB_W_RD ); break; + case king: HASH_KEY ^= w_king_rand[to] ^ w_king_rand[from]; + BOARD[to] = - king; + SQ_WKING = (char)to; + SetClear( BB_W_HDK ); break; + case pro_pawn: NocapNoproW( PRO_PAWN, pro_pawn); + SetClear( BB_WTGOLD ); break; + case pro_lance: NocapNoproW( PRO_LANCE, pro_lance); + SetClear( BB_WTGOLD ); break; + case pro_knight: NocapNoproW( PRO_KNIGHT, pro_knight); + SetClear( BB_WTGOLD ); break; + case pro_silver: NocapNoproW( PRO_SILVER, pro_silver); + SetClear( BB_WTGOLD ); break; + case horse: NocapNoproW( HORSE, horse ); + SetClear( BB_W_HDK ); + SetClear( BB_W_BH ); break; + default: NocapNoproW( DRAGON, dragon ); + SetClear( BB_W_HDK ); + SetClear( BB_W_RD ); break; + } + + if ( ipiece_cap ) + { + switch( ipiece_cap ) + { + case pawn: CapB( PAWN, pawn, pawn ); + Xor( to-nfile, BB_BPAWN_ATK ); break; + case lance: CapB( LANCE, lance, lance ); break; + case knight: CapB( KNIGHT, knight, knight ); break; + case silver: CapB( SILVER, silver, silver ); break; + case gold: CapB( GOLD, gold, gold ); + Xor( to, BB_BTGOLD ); break; + case bishop: CapB( BISHOP, bishop, bishop ); + Xor( to, BB_B_BH ); break; + case rook: CapB( ROOK, rook, rook ); + Xor( to, BB_B_RD ); break; + case pro_pawn: CapB( PRO_PAWN, pawn, pro_pawn ); + Xor( to, BB_BTGOLD ); break; + case pro_lance: CapB( PRO_LANCE, lance, pro_lance ); + Xor( to, BB_BTGOLD ); break; + case pro_knight: CapB( PRO_KNIGHT, knight, pro_knight ); + Xor( to, BB_BTGOLD ); break; + case pro_silver: CapB( PRO_SILVER, silver, pro_silver ); + Xor( to, BB_BTGOLD ); break; + case horse: CapB( HORSE, bishop, horse ); + Xor( to, BB_B_HDK ); + Xor( to, BB_B_BH ); break; + default: CapB( DRAGON, rook, dragon ); + Xor( to, BB_B_HDK ); + Xor( to, BB_B_RD ); break; + } + Xor( to, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag1( from, OCCUPIED_DIAG1 ); + XorDiag2( from, OCCUPIED_DIAG2 ); + } + else { + SetClearFile( from, to, OCCUPIED_FILE ); + SetClearDiag1( from, to, OCCUPIED_DIAG1 ); + SetClearDiag2( from, to, OCCUPIED_DIAG2 ); + } + } + + assert( exam_bb( ptree ) ); +} + +#undef DropB +#undef DropW +#undef CapB +#undef CapW +#undef NocapProB +#undef NocapProW +#undef NocapNoproB +#undef NocapNoproW + +/* + * flag_detect_hang + * flag_rep + * flag_time + * flag_nomake_move + * flag_history + * flag_rejections + */ +int +make_move_root( tree_t * restrict ptree, unsigned int move, int flag ) +{ + int check, drawn, iret, i, n; + + ptree->save_material[0] = (short)MATERIAL; + MakeMove( root_turn, move, 1 ); + + /* detect hang-king */ + if ( ( flag & flag_detect_hang ) && InCheck(root_turn) ) + { + str_error = str_king_hang; + UnMakeMove( root_turn, move, 1 ); + return -2; + } + + drawn = 0; + check = InCheck( Flip(root_turn) ); + ptree->move_last[1] = ptree->move_last[0]; + if ( check ) + { + ptree->nsuc_check[2] = (unsigned char)( ptree->nsuc_check[0] + 1U ); + } + else { ptree->nsuc_check[2] = 0; } + + /* detect repetitions */ + if ( flag & flag_rep ) + { + switch ( detect_repetition( ptree, 2, Flip(root_turn), 3 ) ) + { + case perpetual_check: + str_error = str_perpet_check; + UnMakeMove( root_turn, move, 1 ); + return -2; + + case four_fold_rep: + drawn = 1; + break; + } + } + + /* return, since all of rule-checks were done */ + if ( flag & flag_nomake_move ) + { + UnMakeMove( root_turn, move, 1 ); + return drawn ? 2 : 1; + } + + if ( drawn ) { game_status |= flag_drawn; } + + /* renovate time */ + if ( flag & flag_time ) + { + iret = renovate_time( root_turn ); + if ( iret < 0 ) { return iret; } + } + + root_turn = Flip( root_turn ); + + /* detect checkmate */ + if ( check && is_mate( ptree, 1 ) ) { game_status |= flag_mated; } + + /* save history */ + if ( flag & flag_history ) + { + /* save history for book learning */ + if ( record_game.moves < HASH_REG_HIST_LEN ) + { + history_book_learn[ record_game.moves ].move_played = move; + history_book_learn[ record_game.moves ].hand_played + = ptree->rep_hand_list[ root_nrep ]; + history_book_learn[ record_game.moves ].key_played + = (unsigned int)ptree->rep_board_list[ root_nrep ]; + } + + out_CSA( ptree, &record_game, move ); + } + + /* add rejections */ + if ( flag & flag_rejections ) { add_rejections_root( ptree, move ); } + + /* renew repetition table */ + n = root_nrep; + if ( n >= REP_HIST_LEN - PLY_MAX -1 ) + { + for ( i = 0; i < n; i++ ) + { + ptree->rep_board_list[i] = ptree->rep_board_list[i+1]; + ptree->rep_hand_list[i] = ptree->rep_hand_list[i+1]; + } + } + else { root_nrep++; } + + ptree->nsuc_check[PLY_MAX] = ptree->nsuc_check[0]; + ptree->nsuc_check[0] = ptree->nsuc_check[1]; + ptree->nsuc_check[1] = ptree->nsuc_check[2]; + + /* renovate pv */ + last_root_value_save = last_root_value; + last_pv_save = last_pv; + if ( last_pv.a[1] == move && last_pv.length >= 2 ) + { + if ( last_pv.depth ) + { +#if PLY_INC == EXT_CHECK + if ( ! check ) +#endif + last_pv.depth--; + } + last_pv.length--; + memmove( &(last_pv.a[1]), &(last_pv.a[2]), + last_pv.length * sizeof( unsigned int ) ); + } + else { + last_pv.a[0] = 0; + last_pv.a[1] = 0; + last_pv.depth = 0; + last_pv.length = 0; + last_root_value = 0; + } + + return 1; +} + + +void +unmake_move_root( tree_t * restrict ptree, unsigned int move ) +{ + last_root_value = last_root_value_save; + last_pv = last_pv_save; + + ptree->nsuc_check[1] = ptree->nsuc_check[0]; + ptree->nsuc_check[0] = ptree->nsuc_check[PLY_MAX]; + + root_nrep -= 1; + game_status &= ~( flag_drawn | flag_mated ); + root_turn = Flip(root_turn); + + ptree->save_material[1] = ptree->save_material[0]; + UnMakeMove( root_turn, move, 1 ); + + sub_rejections_root( ptree, move ); +} diff --git a/mate1ply.c b/mate1ply.c new file mode 100644 index 0000000..b09a6d1 --- /dev/null +++ b/mate1ply.c @@ -0,0 +1,2155 @@ +#include +#include +#include "shogi.h" + +#define DebugOut { static int count = 0; \ + if ( count++ < 16 ) { out_CSA_posi( ptree, stdout, 0 ); } } + +static int can_w_king_escape( tree_t * restrict ptree, int to, bitboard_t bb ); +static int can_b_king_escape( tree_t * restrict ptree, int to, bitboard_t bb ); +static int can_w_piece_capture( const tree_t * restrict ptree, int to ); +static int can_b_piece_capture( const tree_t * restrict ptree, int to ); + + +unsigned int +is_b_mate_in_1ply( tree_t * restrict ptree ) +{ + bitboard_t bb, bb_temp, bb_check, bb_check_pro, bb_attacks, bb_drop, bb_move; + unsigned int ubb; + int to, from, idirec; + + assert( ! is_black_attacked( ptree, SQ_BKING ) ); + + /* Drops */ + BBOr( bb_drop, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_drop, bb_drop ); + + if ( IsHandRook(HAND_B) ) { + + BBAnd( bb, abb_w_gold_attacks[SQ_WKING], + abb_b_gold_attacks[SQ_WKING] ); + BBAnd( bb, bb, bb_drop ); + while( BBToU(bb) ) + { + to = FirstOne( bb ); + Xor( to, bb ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_w_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(rook); + } + + } else if ( IsHandLance(HAND_B) && SQ_WKING <= I2 ) { + + to = SQ_WKING+nfile; + if ( ! BOARD[to] && is_white_attacked( ptree, to ) ) + { + bb_attacks = abb_file_attacks[to][0]; + if ( ! can_w_king_escape( ptree, to, bb_attacks ) + && ! can_w_piece_capture( ptree, to ) ) + { + return To2Move(to) | Drop2Move(lance); + } + } + } + + if ( IsHandBishop(HAND_B) ) { + + BBAnd( bb, abb_w_silver_attacks[SQ_WKING], + abb_b_silver_attacks[SQ_WKING] ); + BBAnd( bb, bb, bb_drop ); + while( BBToU(bb) ) + { + to = FirstOne( bb ); + Xor( to, bb ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_w_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(bishop); + } + } + + if ( IsHandGold(HAND_B) ) { + + if ( IsHandRook(HAND_B) ) + { + BBAnd( bb, abb_b_gold_attacks[SQ_WKING], + abb_b_silver_attacks[SQ_WKING] ); + BBNot( bb, bb ); + BBAnd( bb, bb, bb_drop ); + BBAnd( bb, bb, abb_w_gold_attacks[SQ_WKING] ); + } + else { BBAnd( bb, bb_drop, abb_w_gold_attacks[SQ_WKING] ); } + + while ( BBToU(bb) ) + { + to = FirstOne( bb ); + Xor( to, bb ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_w_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(gold); + } + } + + if ( IsHandSilver(HAND_B) ) { + + if ( IsHandGold(HAND_B) ) + { + if ( IsHandBishop(HAND_B) ) { goto b_silver_drop_end; } + BBNot( bb, abb_w_gold_attacks[SQ_WKING] ); + BBAnd( bb, bb, abb_w_silver_attacks[SQ_WKING] ); + BBAnd( bb, bb, bb_drop ); + } + else { + BBAnd( bb, bb_drop, abb_w_silver_attacks[SQ_WKING] ); + if ( IsHandBishop(HAND_B) ) + { + BBAnd( bb, bb, abb_w_gold_attacks[SQ_WKING] ); + } + } + + while ( BBToU(bb) ) + { + to = FirstOne( bb ); + Xor( to, bb ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_silver_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_w_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(silver); + } + } + b_silver_drop_end: + + if ( IsHandKnight(HAND_B) ) { + + BBAnd( bb, bb_drop, abb_w_knight_attacks[SQ_WKING] ); + while ( BBToU(bb) ) + { + to = FirstOne( bb ); + Xor( to, bb ); + + BBIni( bb_attacks ); + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_w_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(knight); + } + } + + /* Moves */ + BBNot( bb_move, BB_BOCCUPY ); + + bb = BB_BDRAGON; + while ( BBToU(bb) ) { + from = FirstOne( bb ); + Xor( from, bb ); + + AttackDragon( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_WKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_B_HDK ); + Xor( from, BB_B_RD ); + Xor( from, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + if ( (int)adirec[SQ_WKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + } + else { AttackDragon( bb_attacks, to ); } + + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_B_RD ); + Xor( from, BB_B_HDK ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(dragon) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_B_RD ); + Xor( from, BB_B_HDK ); + } + + bb.p[0] = BB_BROOK.p[0]; + while ( bb.p[0] ) { + from = last_one0( bb.p[0] ); + bb.p[0] ^= abb_mask[from].p[0]; + + AttackRook( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_WKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + BB_B_RD.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + if ( (int)adirec[SQ_WKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + } + else { AttackDragon( bb_attacks, to ); } + + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_B_RD.p[0] ^= abb_mask[from].p[0]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(rook) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_B_RD.p[0] ^= abb_mask[from].p[0]; + } + + bb.p[1] = BB_BROOK.p[1]; + bb.p[2] = BB_BROOK.p[2]; + while ( bb.p[1] | bb.p[2] ) { + from = first_one12( bb.p[1], bb.p[2] ); + bb.p[1] ^= abb_mask[from].p[1]; + bb.p[2] ^= abb_mask[from].p[2]; + + AttackRook( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + bb_check.p[0] &= abb_king_attacks[SQ_WKING].p[0]; + bb_check.p[1] &= abb_b_gold_attacks[SQ_WKING].p[1]; + bb_check.p[2] &= abb_b_gold_attacks[SQ_WKING].p[2]; + bb_check.p[1] &= abb_w_gold_attacks[SQ_WKING].p[1]; + bb_check.p[2] &= abb_w_gold_attacks[SQ_WKING].p[2]; + if ( ! BBToU(bb_check) ) { continue; } + + BB_B_RD.p[1] ^= abb_mask[from].p[1]; + BB_B_RD.p[2] ^= abb_mask[from].p[2]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + if ( to <= I7 ) { + if ( (int)adirec[SQ_WKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + bb_attacks.p[0] |= abb_king_attacks[to].p[0]; + bb_attacks.p[1] |= abb_king_attacks[to].p[1]; + } + else { AttackDragon( bb_attacks, to ); } + + } else { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + } + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_B_RD.p[1] ^= abb_mask[from].p[1]; + BB_B_RD.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) + | ( (to < A6) ? FLAG_PROMO : 0 ) + | Cap2Move(-BOARD[to]) | Piece2Move(rook) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_B_RD.p[1] ^= abb_mask[from].p[1]; + BB_B_RD.p[2] ^= abb_mask[from].p[2]; + } + + bb = BB_BHORSE; + while ( BBToU(bb) ) { + from = FirstOne( bb ); + Xor( from, bb ); + + AttackHorse( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_WKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_B_HDK ); + Xor( from, BB_B_BH ); + Xor( from, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_B_BH ); + Xor( from, BB_B_HDK ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(horse) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_B_BH ); + Xor( from, BB_B_HDK ); + } + + bb.p[0] = BB_BBISHOP.p[0]; + while ( bb.p[0] ) { + from = last_one0( bb.p[0] ); + bb.p[0] ^= abb_mask[from].p[0]; + + AttackBishop( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_WKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + BB_B_BH.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_B_BH.p[0] ^= abb_mask[from].p[0]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(bishop) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_B_BH.p[0] ^= abb_mask[from].p[0]; + } + + bb.p[1] = BB_BBISHOP.p[1]; + bb.p[2] = BB_BBISHOP.p[2]; + while ( bb.p[1] | bb.p[2] ) { + from = first_one12( bb.p[1], bb.p[2] ); + bb.p[1] ^= abb_mask[from].p[1]; + bb.p[2] ^= abb_mask[from].p[2]; + + AttackBishop( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + bb_check.p[0] &= abb_king_attacks[SQ_WKING].p[0]; + bb_check.p[1] &= abb_b_silver_attacks[SQ_WKING].p[1]; + bb_check.p[2] &= abb_b_silver_attacks[SQ_WKING].p[2]; + bb_check.p[1] &= abb_w_silver_attacks[SQ_WKING].p[1]; + bb_check.p[2] &= abb_w_silver_attacks[SQ_WKING].p[2]; + if ( ! BBToU(bb_check) ) { continue; } + + BB_B_BH.p[1] ^= abb_mask[from].p[1]; + BB_B_BH.p[2] ^= abb_mask[from].p[2]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + if ( to <= I7 ) { + bb_attacks.p[0] |= abb_king_attacks[to].p[0]; + bb_attacks.p[1] |= abb_king_attacks[to].p[1]; + } + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_B_BH.p[1] ^= abb_mask[from].p[1]; + BB_B_BH.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) + | ( (to < A6) ? FLAG_PROMO : 0 ) + | Cap2Move(-BOARD[to]) | Piece2Move(bishop) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_B_BH.p[1] ^= abb_mask[from].p[1]; + BB_B_BH.p[2] ^= abb_mask[from].p[2]; + } + + BBAnd( bb, BB_BTGOLD, b_chk_tbl[SQ_WKING].gold ); + while ( BBToU(bb) ) { + from = FirstOne( bb ); + Xor( from, bb ); + + BBAnd( bb_check, bb_move, abb_b_gold_attacks[from] ); + BBAnd( bb_check, bb_check, abb_w_gold_attacks[SQ_WKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_BTGOLD ); + Xor( from, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_BTGOLD ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(BOARD[from]) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_BTGOLD ); + } + + BBAnd( bb, BB_BSILVER, b_chk_tbl[SQ_WKING].silver ); + while ( bb.p[0] ) { + from = last_one0( bb.p[0] ); + bb.p[0] ^= abb_mask[from].p[0]; + + bb_check_pro.p[0] = bb_move.p[0] & abb_b_silver_attacks[from].p[0] + & abb_w_gold_attacks[SQ_WKING].p[0]; + bb_check_pro.p[1] = bb_move.p[1] & abb_b_silver_attacks[from].p[1] + & abb_w_gold_attacks[SQ_WKING].p[1]; + + bb_check.p[0] = bb_move.p[0] & abb_b_silver_attacks[from].p[0] + & abb_w_silver_attacks[SQ_WKING].p[0] + & ~abb_w_gold_attacks[SQ_WKING].p[0]; + bb_check.p[1] = bb_move.p[1] & abb_b_silver_attacks[from].p[1] + & abb_w_silver_attacks[SQ_WKING].p[1] + & ~abb_w_gold_attacks[SQ_WKING].p[1]; + + if ( ! ( bb_check_pro.p[0] | bb_check_pro.p[1] + | bb_check.p[0]| bb_check.p[1] ) ) { continue; } + + BB_BSILVER.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + while ( bb_check_pro.p[0] | bb_check_pro.p[1] ) { + to = first_one01( bb_check_pro.p[0], bb_check_pro.p[1] ); + bb_check_pro.p[0] ^= abb_mask[to].p[0]; + bb_check_pro.p[1] ^= abb_mask[to].p[1]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BSILVER.p[0] ^= abb_mask[from].p[0]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(silver) ); + } + + while ( bb_check.p[0] | bb_check.p[1] ) { + to = first_one01( bb_check.p[0], bb_check.p[1] ); + bb_check.p[0] ^= abb_mask[to].p[0]; + bb_check.p[1] ^= abb_mask[to].p[1]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_silver_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BSILVER.p[0] ^= abb_mask[from].p[0]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(silver) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BSILVER.p[0] ^= abb_mask[from].p[0]; + } + + ubb = bb.p[1] & 0x7fc0000U; + while ( ubb ) { + from = last_one1( ubb ); + ubb ^= abb_mask[from].p[1]; + + bb_check_pro.p[0] = bb_move.p[0] & abb_b_silver_attacks[from].p[0] + & abb_w_gold_attacks[SQ_WKING].p[0]; + + bb_check.p[0] = bb_move.p[0] & abb_b_silver_attacks[from].p[0] + & abb_w_silver_attacks[SQ_WKING].p[0] + & ~abb_w_gold_attacks[SQ_WKING].p[0]; + bb_check.p[1] = bb_move.p[1] & abb_b_silver_attacks[from].p[1] + & abb_w_silver_attacks[SQ_WKING].p[1]; + + if ( ! (bb_check_pro.p[0]|bb_check.p[0]|bb_check.p[1]) ) { continue; } + + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + while ( bb_check_pro.p[0] ) { + to = last_one0( bb_check_pro.p[0] ); + bb_check_pro.p[0] ^= abb_mask[to].p[0]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(silver) ); + } + + while ( bb_check.p[0] | bb_check.p[1] ) { + to = first_one01( bb_check.p[0], bb_check.p[1] ); + bb_check.p[0] ^= abb_mask[to].p[0]; + bb_check.p[1] ^= abb_mask[to].p[1]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_silver_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(silver) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + } + + bb.p[1] &= 0x003ffffU; + while ( bb.p[1] | bb.p[2] ) { + from = first_one12( bb.p[1], bb.p[2] ); + bb.p[1] ^= abb_mask[from].p[1]; + bb.p[2] ^= abb_mask[from].p[2]; + + bb_check.p[1] = bb_move.p[1] & abb_b_silver_attacks[from].p[1] + & abb_w_silver_attacks[SQ_WKING].p[1]; + bb_check.p[2] = bb_move.p[2] & abb_b_silver_attacks[from].p[2] + & abb_w_silver_attacks[SQ_WKING].p[2]; + if ( ! ( bb_check.p[1] | bb_check.p[2] ) ) { continue; } + + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[2] ^= abb_mask[from].p[2]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = first_one12( bb_check.p[1], bb_check.p[2] ); + bb_check.p[1] ^= abb_mask[to].p[1]; + bb_check.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_silver_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(silver) ); + } while ( bb_check.p[1] | bb_check.p[2] ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BSILVER.p[1] ^= abb_mask[from].p[1]; + BB_BSILVER.p[2] ^= abb_mask[from].p[2]; + } + + BBAnd( bb, BB_BKNIGHT, b_chk_tbl[SQ_WKING].knight ); + while ( BBToU(bb) ) { + from = FirstOne( bb ); + Xor( from, bb ); + + bb_check.p[0] = bb_move.p[0] & abb_b_knight_attacks[from].p[0] + & abb_w_gold_attacks[SQ_WKING].p[0]; + + if ( bb_check.p[0] ) { + BB_BKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = last_one0( bb_check.p[0] ); + bb_check.p[0] ^= abb_mask[to].p[0]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(knight) ); + } while ( bb_check.p[0] ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + } else { + + BBAnd( bb_check, bb_move, abb_b_knight_attacks[from] ); + BBAnd( bb_check, bb_check, abb_w_knight_attacks[SQ_WKING] ); + + if ( BBToU(bb_check) ) { + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_BKNIGHT.p[2] ^= abb_mask[from].p[2]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = FirstOne( bb_check ); + Xor( to, bb_check ); + + BBIni( bb_attacks ); + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_BKNIGHT.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(knight) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_BKNIGHT.p[2] ^= abb_mask[from].p[2]; + } + } + } + + BBAnd( bb, BB_BLANCE, b_chk_tbl[SQ_WKING].lance ); + while ( BBToU(bb) ) { + from = FirstOne( bb ); + Xor( from, bb ); + + bb_attacks = AttackFile(from); + BBAnd( bb_attacks, bb_attacks, abb_minus_rays[from] ); + BBAnd( bb_attacks, bb_attacks, bb_move ); + + BBAnd( bb_check, bb_attacks, abb_mask[SQ_WKING+nfile] ); + bb_check_pro.p[0] = bb_attacks.p[0] & abb_w_gold_attacks[SQ_WKING].p[0]; + + if ( ! ( bb_check_pro.p[0] | bb_check.p[0] + | bb_check.p[1] | bb_check.p[2] ) ) { continue; } + + Xor( from, BB_BLANCE ); + Xor( from, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + bb_check.p[0] &= 0x1ffU; + if ( BBToU(bb_check) ) { + + to = SQ_WKING+nfile; + if ( ! is_white_attacked( ptree, to ) ) { + bb_check.p[0] &= ~abb_mask[to].p[0]; + goto b_lance_next; + } + bb_temp = abb_file_attacks[to][0]; + if ( can_w_king_escape( ptree, to, bb_temp ) ) { goto b_lance_next; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { goto b_lance_next; } + if ( IsDiscoverBK( from, to ) ) { goto b_lance_next; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_BLANCE ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(lance) ); + } + + b_lance_next: + while ( bb_check_pro.p[0] ) + { + to = last_one0( bb_check_pro.p[0] ); + bb_check_pro.p[0] ^= abb_mask[to].p[0]; + + if ( ! is_white_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverBK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_BLANCE ); + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(lance) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_BOCCUPY ); + Xor( from, BB_BLANCE ); + } + + bb_check.p[0] = bb_move.p[0] & BB_BPAWN_ATK.p[0] + & abb_w_gold_attacks[SQ_WKING].p[0]; + while ( bb_check.p[0] ) { + to = last_one0( bb_check.p[0] ); + from = to + nfile; + bb_check.p[0] ^= abb_mask[to].p[0]; + + BB_BPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + if ( ! is_white_attacked( ptree, to ) ) { goto b_pawn_pro_next; } + bb_attacks = abb_b_gold_attacks[to]; + if ( can_w_king_escape( ptree,to,bb_attacks ) ) { goto b_pawn_pro_next; } + if ( IsDiscoverWK( from, to ) ); + else if ( can_w_piece_capture( ptree, to ) ) { goto b_pawn_pro_next; } + if ( IsDiscoverBK( from, to ) ) { goto b_pawn_pro_next; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(-BOARD[to]) | Piece2Move(pawn) ); + + b_pawn_pro_next: + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + } + + if ( SQ_WKING >= A7 && SQ_WKING <= I3 ) { + to = SQ_WKING + nfile; + from = to + nfile; + if ( BOARD[from] == pawn && BOARD[to] <= 0 ) { + + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_BPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + if ( ! is_white_attacked( ptree, to ) ) { goto b_pawn_end; } + BBIni( bb_attacks ); + if ( can_w_king_escape( ptree,to,bb_attacks ) ) { goto b_pawn_end; } + if ( can_w_piece_capture( ptree, to ) ) { goto b_pawn_end; } + if ( IsDiscoverBK( from, to ) ) { goto b_pawn_end; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_BPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(pawn) ); + + b_pawn_end: + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_BOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_BOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_BPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_BPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + } + } + + return 0; +} + + +unsigned int +is_w_mate_in_1ply( tree_t * restrict ptree ) +{ + bitboard_t bb, bb_temp, bb_check, bb_check_pro, bb_attacks, bb_drop, bb_move; + unsigned int ubb; + int to, from, idirec; + + assert( ! is_white_attacked( ptree, SQ_WKING ) ); + + /* Drops */ + BBOr( bb_drop, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_drop, bb_drop ); + + if ( IsHandRook(HAND_W) ) { + + BBAnd( bb, abb_w_gold_attacks[SQ_BKING], + abb_b_gold_attacks[SQ_BKING] ); + BBAnd( bb, bb, bb_drop ); + while( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_b_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(rook); + } + + } else if ( IsHandLance(HAND_W) && SQ_BKING >= A8 ) { + + to = SQ_BKING-nfile; + if ( ( ! BOARD[to] ) && is_black_attacked( ptree, to ) ) + { + bb_attacks = abb_file_attacks[to][0]; + if ( ( ! can_b_king_escape( ptree, to, bb_attacks ) ) + && ( ! can_b_piece_capture( ptree, to ) ) ) + { + return To2Move(to) | Drop2Move(lance); + } + } + } + + if ( IsHandBishop(HAND_W) ) { + + BBAnd( bb, abb_w_silver_attacks[SQ_BKING], + abb_b_silver_attacks[SQ_BKING] ); + BBAnd( bb, bb, bb_drop ); + while( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_b_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(bishop); + } + } + + if ( IsHandGold(HAND_W) ) { + + if ( IsHandRook(HAND_W) ) + { + BBAnd( bb, abb_w_gold_attacks[SQ_BKING], + abb_w_silver_attacks[SQ_BKING] ); + BBNot( bb, bb ); + BBAnd( bb, bb, bb_drop ); + BBAnd( bb, bb, abb_b_gold_attacks[SQ_BKING] ); + } + else { BBAnd( bb, bb_drop, abb_b_gold_attacks[SQ_BKING] ); } + + while ( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_b_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(gold); + } + } + + if ( IsHandSilver(HAND_W) ) { + + if ( IsHandGold(HAND_W) ) + { + if ( IsHandBishop(HAND_W) ) { goto w_silver_drop_end; } + BBNot( bb, abb_b_gold_attacks[SQ_BKING] ); + BBAnd( bb, bb, abb_b_silver_attacks[SQ_BKING] ); + BBAnd( bb, bb, bb_drop ); + } + else { + BBAnd( bb, bb_drop, abb_b_silver_attacks[SQ_BKING] ); + if ( IsHandBishop(HAND_W) ) + { + BBAnd( bb, bb, abb_b_gold_attacks[SQ_BKING] ); + } + } + + while ( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_silver_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_b_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(silver); + } + } + w_silver_drop_end: + + if ( IsHandKnight(HAND_W) ) { + + BBAnd( bb, bb_drop, abb_b_knight_attacks[SQ_BKING] ); + while ( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + BBIni( bb_attacks ); + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( can_b_piece_capture( ptree, to ) ) { continue; } + return To2Move(to) | Drop2Move(knight); + } + } + + /* Moves */ + BBNot( bb_move, BB_WOCCUPY ); + + bb = BB_WDRAGON; + while ( BBToU(bb) ) { + from = LastOne( bb ); + Xor( from, bb ); + + AttackDragon( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_BKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_W_HDK ); + Xor( from, BB_W_RD ); + Xor( from, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + if ( (int)adirec[SQ_BKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + } + else { AttackDragon( bb_attacks, to ); } + + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_W_RD ); + Xor( from, BB_W_HDK ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(dragon) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_W_RD ); + Xor( from, BB_W_HDK ); + } + + bb.p[2] = BB_WROOK.p[2]; + while ( bb.p[2] ) { + from = first_one2( bb.p[2] ); + bb.p[2] ^= abb_mask[from].p[2]; + + AttackRook( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_BKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + BB_W_RD.p[2] ^= abb_mask[from].p[2]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + if ( (int)adirec[SQ_BKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + } + else { AttackDragon( bb_attacks, to ); } + + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_W_RD.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(rook) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_W_RD.p[2] ^= abb_mask[from].p[2]; + } + + bb.p[0] = BB_WROOK.p[0]; + bb.p[1] = BB_WROOK.p[1]; + while ( bb.p[0] | bb.p[1] ) { + from = last_one01( bb.p[0], bb.p[1] ); + bb.p[0] ^= abb_mask[from].p[0]; + bb.p[1] ^= abb_mask[from].p[1]; + + AttackRook( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + bb_check.p[0] &= abb_b_gold_attacks[SQ_BKING].p[0]; + bb_check.p[1] &= abb_b_gold_attacks[SQ_BKING].p[1]; + bb_check.p[0] &= abb_w_gold_attacks[SQ_BKING].p[0]; + bb_check.p[1] &= abb_w_gold_attacks[SQ_BKING].p[1]; + bb_check.p[2] &= abb_king_attacks[SQ_BKING].p[2]; + if ( ! BBToU(bb_check) ) { continue; } + + BB_W_RD.p[0] ^= abb_mask[from].p[0]; + BB_W_RD.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + if ( to >= A3 ) { + if ( (int)adirec[SQ_BKING][to] & flag_cross ) + { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + bb_attacks.p[1] |= abb_king_attacks[to].p[1]; + bb_attacks.p[2] |= abb_king_attacks[to].p[2]; + } + else { AttackDragon( bb_attacks, to ); } + } else { + bb_attacks = abb_file_attacks[to][0]; + bb_attacks.p[aslide[to].ir0] |= ai_rook_attacks_r0[to][0]; + } + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_W_RD.p[0] ^= abb_mask[from].p[0]; + BB_W_RD.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | ( (to > I4) ? FLAG_PROMO : 0 ) + | Cap2Move(BOARD[to]) | Piece2Move(rook) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_W_RD.p[0] ^= abb_mask[from].p[0]; + BB_W_RD.p[1] ^= abb_mask[from].p[1]; + } + + bb = BB_WHORSE; + while ( BBToU(bb) ) { + from = LastOne( bb ); + Xor( from, bb ); + + AttackHorse( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_BKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_W_HDK ); + Xor( from, BB_W_BH ); + Xor( from, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_W_BH ); + Xor( from, BB_W_HDK ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(horse) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_W_BH ); + Xor( from, BB_W_HDK ); + } + + bb.p[2] = BB_WBISHOP.p[2]; + while ( bb.p[2] ) { + from = first_one2( bb.p[2] ); + bb.p[2] ^= abb_mask[from].p[2]; + + AttackBishop( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + BBAnd( bb_check, bb_check, abb_king_attacks[SQ_BKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + BB_W_BH.p[2] ^= abb_mask[from].p[2]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + BBOr( bb_attacks, bb_attacks, abb_king_attacks[to] ); + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_W_BH.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(bishop) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_W_BH.p[2] ^= abb_mask[from].p[2]; + } + + bb.p[0] = BB_WBISHOP.p[0]; + bb.p[1] = BB_WBISHOP.p[1]; + while ( bb.p[0] | bb.p[1] ) { + from = last_one01( bb.p[0], bb.p[1] ); + bb.p[0] ^= abb_mask[from].p[0]; + bb.p[1] ^= abb_mask[from].p[1]; + + AttackBishop( bb_attacks, from ); + BBAnd( bb_check, bb_move, bb_attacks ); + bb_check.p[0] &= abb_b_silver_attacks[SQ_BKING].p[0]; + bb_check.p[1] &= abb_b_silver_attacks[SQ_BKING].p[1]; + bb_check.p[0] &= abb_w_silver_attacks[SQ_BKING].p[0]; + bb_check.p[1] &= abb_w_silver_attacks[SQ_BKING].p[1]; + bb_check.p[2] &= abb_king_attacks[SQ_BKING].p[2]; + if ( ! BBToU(bb_check) ) { continue; } + + BB_W_BH.p[0] ^= abb_mask[from].p[0]; + BB_W_BH.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + BBOr( bb_attacks, abb_bishop_attacks_rr45[to][0], + abb_bishop_attacks_rl45[to][0] ); + if ( to >= A3 ) { + bb_attacks.p[1] |= abb_king_attacks[to].p[1]; + bb_attacks.p[2] |= abb_king_attacks[to].p[2]; + } + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_W_BH.p[0] ^= abb_mask[from].p[0]; + BB_W_BH.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | ( (to > I4) ? FLAG_PROMO : 0 ) + | Cap2Move(BOARD[to]) | Piece2Move(bishop) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_W_BH.p[0] ^= abb_mask[from].p[0]; + BB_W_BH.p[1] ^= abb_mask[from].p[1]; + } + + BBAnd( bb, BB_WTGOLD, w_chk_tbl[SQ_BKING].gold ); + while ( BBToU(bb) ) { + from = LastOne( bb ); + Xor( from, bb ); + + BBAnd( bb_check, bb_move, abb_w_gold_attacks[from] ); + BBAnd( bb_check, bb_check, abb_b_gold_attacks[SQ_BKING] ); + if ( ! BBToU(bb_check) ) { continue; } + + Xor( from, BB_WTGOLD ); + Xor( from, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_WTGOLD ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(-BOARD[from]) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_WTGOLD ); + } + + BBAnd( bb, BB_WSILVER, w_chk_tbl[SQ_BKING].silver ); + while ( bb.p[2] ) { + from = first_one2( bb.p[2] ); + bb.p[2] ^= abb_mask[from].p[2]; + + bb_check_pro.p[1] = bb_move.p[1] & abb_w_silver_attacks[from].p[1] + & abb_b_gold_attacks[SQ_BKING].p[1]; + bb_check_pro.p[2] = bb_move.p[2] & abb_w_silver_attacks[from].p[2] + & abb_b_gold_attacks[SQ_BKING].p[2]; + + bb_check.p[1] = bb_move.p[1] & abb_w_silver_attacks[from].p[1] + & abb_b_silver_attacks[SQ_BKING].p[1] + & ~abb_b_gold_attacks[SQ_BKING].p[1]; + bb_check.p[2] = bb_move.p[2] & abb_w_silver_attacks[from].p[2] + & abb_b_silver_attacks[SQ_BKING].p[2] + & ~abb_b_gold_attacks[SQ_BKING].p[2]; + + if ( ! ( bb_check_pro.p[1] | bb_check_pro.p[2] + | bb_check.p[1]| bb_check.p[2] ) ) { continue; } + + BB_WSILVER.p[2] ^= abb_mask[from].p[2]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + while ( bb_check_pro.p[1] | bb_check_pro.p[2] ) { + to = last_one12( bb_check_pro.p[1], bb_check_pro.p[2] ); + bb_check_pro.p[1] ^= abb_mask[to].p[1]; + bb_check_pro.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WSILVER.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(silver) ); + } + + while ( bb_check.p[1] | bb_check.p[2] ) { + to = last_one12( bb_check.p[1], bb_check.p[2] ); + bb_check.p[1] ^= abb_mask[to].p[1]; + bb_check.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_silver_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WSILVER.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(silver) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WSILVER.p[2] ^= abb_mask[from].p[2]; + } + + ubb = bb.p[1] & 0x1ffU; + while ( ubb ) { + from = first_one1( ubb ); + ubb ^= abb_mask[from].p[1]; + + bb_check_pro.p[2] = bb_move.p[2] & abb_w_silver_attacks[from].p[2] + & abb_b_gold_attacks[SQ_BKING].p[2]; + + bb_check.p[2] = bb_move.p[2] & abb_w_silver_attacks[from].p[2] + & abb_b_silver_attacks[SQ_BKING].p[2] + & ~abb_b_gold_attacks[SQ_BKING].p[2]; + bb_check.p[1] = bb_move.p[1] & abb_w_silver_attacks[from].p[1] + & abb_b_silver_attacks[SQ_BKING].p[1]; + + if ( ! (bb_check_pro.p[2]|bb_check.p[2]|bb_check.p[1]) ) { continue; } + + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + while ( bb_check_pro.p[2] ) { + to = first_one2( bb_check_pro.p[2] ); + bb_check_pro.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(silver) ); + } + + while ( bb_check.p[1] | bb_check.p[2] ) { + to = last_one12( bb_check.p[1], bb_check.p[2] ); + bb_check.p[1] ^= abb_mask[to].p[1]; + bb_check.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_silver_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(silver) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + } + + bb.p[0] = bb.p[0]; + bb.p[1] = bb.p[1] & 0x7fffe00U; + while ( bb.p[0] | bb.p[1] ) { + from = last_one01( bb.p[0], bb.p[1] ); + bb.p[0] ^= abb_mask[from].p[0]; + bb.p[1] ^= abb_mask[from].p[1]; + + bb_check.p[0] = bb_move.p[0] & abb_w_silver_attacks[from].p[0] + & abb_b_silver_attacks[SQ_BKING].p[0]; + bb_check.p[1] = bb_move.p[1] & abb_w_silver_attacks[from].p[1] + & abb_b_silver_attacks[SQ_BKING].p[1]; + if ( ! ( bb_check.p[0] | bb_check.p[1] ) ) { continue; } + + BB_WSILVER.p[0] ^= abb_mask[from].p[0]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = last_one01( bb_check.p[0], bb_check.p[1] ); + bb_check.p[0] ^= abb_mask[to].p[0]; + bb_check.p[1] ^= abb_mask[to].p[1]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_silver_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WSILVER.p[0] ^= abb_mask[from].p[0]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(silver) ); + } while ( bb_check.p[0] | bb_check.p[1] ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WSILVER.p[0] ^= abb_mask[from].p[0]; + BB_WSILVER.p[1] ^= abb_mask[from].p[1]; + } + + BBAnd( bb, BB_WKNIGHT, w_chk_tbl[SQ_BKING].knight ); + while ( BBToU(bb) ) { + from = LastOne( bb ); + Xor( from, bb ); + + bb_check.p[2] = bb_move.p[2] & abb_w_knight_attacks[from].p[2] + & abb_b_gold_attacks[SQ_BKING].p[2]; + + if ( bb_check.p[2] ) { + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_WKNIGHT.p[2] ^= abb_mask[from].p[2]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = first_one2( bb_check.p[2] ); + bb_check.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_WKNIGHT.p[2] ^= abb_mask[from].p[2]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(knight) ); + } while ( bb_check.p[2] ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_WKNIGHT.p[2] ^= abb_mask[from].p[2]; + } else { + + BBAnd( bb_check, bb_move, abb_w_knight_attacks[from] ); + BBAnd( bb_check, bb_check, abb_b_knight_attacks[SQ_BKING] ); + + if ( BBToU(bb_check) ) { + BB_WKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + do { + to = LastOne( bb_check ); + Xor( to, bb_check ); + + BBIni( bb_attacks ); + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(knight) ); + } while ( BBToU(bb_check) ); + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WKNIGHT.p[0] ^= abb_mask[from].p[0]; + BB_WKNIGHT.p[1] ^= abb_mask[from].p[1]; + } + } + } + + BBAnd( bb, BB_WLANCE, w_chk_tbl[SQ_BKING].lance ); + while ( BBToU(bb) ) { + from = LastOne( bb ); + Xor( from, bb ); + + bb_attacks = AttackFile(from); + BBAnd( bb_attacks, bb_attacks, abb_plus_rays[from] ); + BBAnd( bb_attacks, bb_attacks, bb_move ); + + BBAnd( bb_check, bb_attacks, abb_mask[SQ_BKING-nfile] ); + bb_check_pro.p[2] = bb_attacks.p[2] & abb_b_gold_attacks[SQ_BKING].p[2]; + + if ( ! ( bb_check_pro.p[2] | bb_check.p[0] + | bb_check.p[1] | bb_check.p[2] ) ) { continue; } + + Xor( from, BB_WLANCE ); + Xor( from, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + bb_check.p[2] &= 0x7fc0000U; + if ( BBToU(bb_check) ) { + + to = SQ_BKING-nfile; + if ( ! is_black_attacked( ptree, to ) ) { + bb_check.p[2] &= ~abb_mask[to].p[2]; + goto w_lance_next; + } + bb_temp = abb_file_attacks[to][0]; + if ( can_b_king_escape( ptree, to, bb_temp ) ) { goto w_lance_next; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { goto w_lance_next; } + if ( IsDiscoverWK( from, to ) ) { goto w_lance_next; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_WLANCE ); + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(lance) ); + } + + w_lance_next: + while ( bb_check_pro.p[2] ) + { + to = first_one2( bb_check_pro.p[2] ); + bb_check_pro.p[2] ^= abb_mask[to].p[2]; + + if ( ! is_black_attacked( ptree, to ) ) { continue; } + + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree, to, bb_attacks ) ) { continue; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { continue; } + if ( IsDiscoverWK( from, to ) ) { continue; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_WLANCE ); + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(lance) ); + } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + Xor( from, BB_WOCCUPY ); + Xor( from, BB_WLANCE ); + } + + bb_check.p[2] = bb_move.p[2] & BB_WPAWN_ATK.p[2] + & abb_b_gold_attacks[SQ_BKING].p[2]; + while ( bb_check.p[2] ) { + to = first_one2( bb_check.p[2] ); + from = to - nfile; + bb_check.p[2] ^= abb_mask[to].p[2]; + + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_WPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + if ( ! is_black_attacked( ptree, to ) ) { goto w_pawn_pro_next; } + bb_attacks = abb_w_gold_attacks[to]; + if ( can_b_king_escape( ptree,to,bb_attacks ) ) { goto w_pawn_pro_next; } + if ( IsDiscoverBK( from, to ) ); + else if ( can_b_piece_capture( ptree, to ) ) { goto w_pawn_pro_next; } + if ( IsDiscoverWK( from, to ) ) { goto w_pawn_pro_next; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_WPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + return ( To2Move(to) | From2Move(from) | FLAG_PROMO + | Cap2Move(BOARD[to]) | Piece2Move(pawn) ); + + w_pawn_pro_next: + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WOCCUPY.p[2] ^= abb_mask[from].p[2]; + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_WPAWN_ATK.p[2] ^= abb_mask[to].p[2]; + } + + if ( SQ_BKING <= I3 && SQ_BKING >= A7 ) { + to = SQ_BKING - nfile; + from = to - nfile; + if ( BOARD[from] == -pawn && BOARD[to] >= 0 ) { + + BB_WPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + + if ( ! is_black_attacked( ptree, to ) ) { goto w_pawn_end; } + BBIni( bb_attacks ); + if ( can_b_king_escape( ptree,to,bb_attacks ) ) { goto w_pawn_end; } + if ( can_b_piece_capture( ptree, to ) ) { goto w_pawn_end; } + if ( IsDiscoverWK( from, to ) ) { goto w_pawn_end; } + + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + return ( To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(pawn) ); + + w_pawn_end: + XorFile( from, OCCUPIED_FILE ); + XorDiag2( from, OCCUPIED_DIAG2 ); + XorDiag1( from, OCCUPIED_DIAG1 ); + BB_WOCCUPY.p[0] ^= abb_mask[from].p[0]; + BB_WOCCUPY.p[1] ^= abb_mask[from].p[1]; + BB_WPAWN_ATK.p[0] ^= abb_mask[to].p[0]; + BB_WPAWN_ATK.p[1] ^= abb_mask[to].p[1]; + } + } + + return 0; +} + + +static int +can_w_piece_capture( const tree_t * restrict ptree, int to ) +{ + bitboard_t bb_sum, bb, bb_attacks; + int idirec, from; + + from = to-nfile; + if ( to >= A8 && BOARD[from] == -pawn ) + { + if ( IsDiscoverWK(from,to) ); + else { return 1; } + } + + BBAnd( bb_sum, BB_WKNIGHT, abb_b_knight_attacks[to] ); + + BBAnd( bb, BB_WSILVER, abb_b_silver_attacks[to] ); + BBOr( bb_sum, bb, bb_sum ); + + BBAnd( bb, BB_WTGOLD, abb_b_gold_attacks[to] ); + BBOr( bb_sum, bb, bb_sum ); + + BBOr( bb, BB_WHORSE, BB_WDRAGON ); + BBAnd( bb, bb, abb_king_attacks[to] ); + BBOr( bb_sum, bb, bb_sum ); + + AttackBishop( bb, to ); + BBAnd( bb, BB_W_BH, bb ); + BBOr( bb_sum, bb, bb_sum ); + + bb_sum.p[aslide[to].ir0] |= BB_W_RD.p[aslide[to].ir0] & AttackRank(to); + + BBAnd( bb, BB_WLANCE, abb_minus_rays[to] ); + BBOr( bb, bb, BB_W_RD ); + bb_attacks = AttackFile( to ); + BBAnd( bb, bb, bb_attacks ); + BBOr( bb_sum, bb_sum, bb ); + + while ( BBToU( bb_sum ) ) + { + from = FirstOne( bb_sum ); + Xor( from, bb_sum ); + + if ( IsDiscoverWK(from,to) ) { continue; } + return 1; + } + + return 0; +} + + +static int +can_b_piece_capture( const tree_t * restrict ptree, int to ) +{ + bitboard_t bb_sum, bb, bb_attacks; + int idirec, from; + + from = to+nfile; + if ( to <= I2 && BOARD[from] == pawn ) + { + if ( IsDiscoverBK(from,to) ); + else { return 1; } + } + + BBAnd( bb_sum, BB_BKNIGHT, abb_w_knight_attacks[to] ); + + BBAnd( bb, BB_BSILVER, abb_w_silver_attacks[to] ); + BBOr( bb_sum, bb_sum, bb ); + + BBAnd( bb, BB_BTGOLD, abb_w_gold_attacks[to] ); + BBOr( bb_sum, bb_sum, bb ); + + BBOr( bb, BB_BHORSE, BB_BDRAGON ); + BBAnd( bb, bb, abb_king_attacks[to] ); + BBOr( bb_sum, bb_sum, bb ); + + AttackBishop( bb, to ); + BBAnd( bb, bb, BB_B_BH ); + BBOr( bb_sum, bb_sum, bb ); + + bb_sum.p[aslide[to].ir0] |= BB_B_RD.p[aslide[to].ir0] & AttackRank(to); + + BBAnd( bb, BB_BLANCE, abb_plus_rays[to] ); + BBOr( bb, bb, BB_B_RD ); + bb_attacks = AttackFile( to ); + BBAnd( bb, bb, bb_attacks ); + BBOr( bb_sum, bb_sum, bb ); + + while ( BBToU( bb_sum ) ) + { + from = LastOne( bb_sum ); + Xor( from, bb_sum ); + + if ( IsDiscoverBK( from, to ) ) { continue; } + return 1; + } + + return 0; +} + + +static int +can_w_king_escape( tree_t * restrict ptree, int to, bitboard_t bb ) +{ + int iret = 0, iescape; + + if ( !BOARD[to] ) + { + Xor( to, BB_BOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + Xor( SQ_WKING, BB_WOCCUPY ); + XorFile( SQ_WKING, OCCUPIED_FILE ); + XorDiag2( SQ_WKING, OCCUPIED_DIAG2 ); + XorDiag1( SQ_WKING, OCCUPIED_DIAG1 ); + + BBOr( bb, bb, abb_mask[to] ); + BBOr( bb, bb, BB_WOCCUPY ); + BBNot( bb, bb ); + BBAnd( bb, bb, abb_king_attacks[SQ_WKING] ); + + while( BBToU(bb) ) + { + iescape = FirstOne( bb ); + if ( ! is_white_attacked( ptree, iescape ) ) + { + iret = 1; + break; + } + Xor( iescape, bb ); + } + + XorFile( SQ_WKING, OCCUPIED_FILE ); + XorDiag2( SQ_WKING, OCCUPIED_DIAG2 ); + XorDiag1( SQ_WKING, OCCUPIED_DIAG1 ); + Xor( SQ_WKING, BB_WOCCUPY ); + if ( !BOARD[to] ) + { + Xor( to, BB_BOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + + return iret; +} + + +static int +can_b_king_escape( tree_t * restrict ptree, int to, bitboard_t bb ) +{ + int iret = 0, iescape; + + if ( !BOARD[to] ) + { + Xor( to, BB_WOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + + Xor( SQ_BKING, BB_BOCCUPY ); + XorFile( SQ_BKING, OCCUPIED_FILE ); + XorDiag2( SQ_BKING, OCCUPIED_DIAG2 ); + XorDiag1( SQ_BKING, OCCUPIED_DIAG1 ); + + BBOr( bb, bb, abb_mask[to] ); + BBOr( bb, bb, BB_BOCCUPY ); + BBNot( bb, bb ); + BBAnd( bb, bb, abb_king_attacks[SQ_BKING] ); + + while( BBToU(bb) ) + { + iescape = LastOne( bb ); + if ( ! is_black_attacked( ptree, iescape ) ) + { + iret = 1; + break; + } + Xor( iescape, bb ); + } + + XorFile( SQ_BKING, OCCUPIED_FILE ); + XorDiag2( SQ_BKING, OCCUPIED_DIAG2 ); + XorDiag1( SQ_BKING, OCCUPIED_DIAG1 ); + Xor( SQ_BKING, BB_BOCCUPY ); + + if ( !BOARD[to] ) + { + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + Xor( to, BB_WOCCUPY ); + } + + return iret; +} diff --git a/mate3.c b/mate3.c new file mode 100644 index 0000000..61d1036 --- /dev/null +++ b/mate3.c @@ -0,0 +1,792 @@ +#include +#include +#include +#include "shogi.h" + +enum { mate_king_cap_checker = 0, + mate_cap_checker_gen, + mate_cap_checker, + mate_king_cap_gen, + mate_king_cap, + mate_king_move_gen, + mate_king_move, + mate_intercept_move_gen, + mate_intercept_move, + mate_intercept_weak_move, + mate_intercept_drop_sup }; + +static int mate3_and( tree_t * restrict ptree, int turn, int ply, int flag ); +static void checker( const tree_t * restrict ptree, char *psq, int turn ); +static unsigned int gen_king_cap_checker( const tree_t * restrict ptree, + int to, int turn ); +static int mate_weak_or( tree_t * restrict ptree, int turn, int ply, + int from, int to ); +static unsigned int *gen_move_to( const tree_t * restrict ptree, int sq, + int turn, unsigned int * restrict pmove ); +static unsigned int *gen_king_move( const tree_t * restrict ptree, + const char *psq, int turn, int is_capture, + unsigned int * restrict pmove ); +static unsigned int *gen_intercept( tree_t * restrict __ptree__, + int sq_checker, int ply, int turn, + int * restrict premaining, + unsigned int * restrict pmove, int flag ); +static int gen_next_evasion_mate( tree_t * restrict ptree, const char *psq, + int ply, int turn, int flag ); + +unsigned int +is_mate_in3ply( tree_t * restrict ptree, int turn, int ply ) +{ + int value, flag_skip; + + if ( ply >= PLY_MAX-2 ) { return 0; } + + flag_skip = 0; + + ptree->anext_move[ply].move_last = ptree->move_last[ply-1]; + ptree->move_last[ply] = GenCheck( turn, ptree->move_last[ply-1] ); + + while ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *ptree->anext_move[ply].move_last++; + + if ( MOVE_CURR & MOVE_CHK_CLEAR ) { flag_skip = 0; } + if ( flag_skip ) { continue; } + + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + MakeMove( turn, MOVE_CURR, ply ); + if ( InCheck(turn) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + + value = mate3_and( ptree, Flip(turn), ply+1, 0 ); + + UnMakeMove( turn, MOVE_CURR, ply ); + + if ( value ) { return 1; } + + if ( ( MOVE_CURR & MOVE_CHK_SET ) + && I2To(MOVE_CURR) != I2To(ptree->current_move[ply+1]) ) + { + flag_skip = 1; + } + } + + return 0; +} + + +static int +mate3_and( tree_t * restrict ptree, int turn, int ply, int flag ) +{ + unsigned int move; + char asq[2]; + + assert( InCheck(turn) ); + + ptree->anext_move[ply].next_phase = mate_king_cap_checker; + checker( ptree, asq, turn ); + + while ( gen_next_evasion_mate( ptree, asq, ply, turn, flag ) ) { + + if ( ptree->anext_move[ply].next_phase == mate_intercept_drop_sup ) + { + return 0; + } + + MakeMove( turn, MOVE_CURR, ply ); + assert( ! InCheck(turn) ); + + if ( InCheck( Flip(turn) ) ) { move = 0; } + else { move = IsMateIn1Ply( Flip(turn) ); } + + if ( ! move + && ptree->anext_move[ply].next_phase == mate_intercept_weak_move ) + { + assert( asq[1] == nsquare ); + move = (unsigned int)mate_weak_or( ptree, Flip(turn), ply+1, asq[0], + I2To(MOVE_CURR) ); + } + + UnMakeMove( turn, MOVE_CURR, ply ); + + if ( ! move ) { return 0; } + } + + return 1; +} + + +static int +mate_weak_or( tree_t * restrict ptree, int turn, int ply, int from, + int to ) +{ + int idirec, pc, pc_cap, value, flag; + + if ( ply >= PLY_MAX-2 ) { return 0; } + + if ( turn ) + { + if ( IsDiscoverWK( from, to ) ) { return 0; } + + pc = -BOARD[from]; + pc_cap = BOARD[to]; + MOVE_CURR = ( To2Move(to) | From2Move(from) + | Piece2Move(pc) | Cap2Move(pc_cap) ); + if ( ( pc == bishop || pc == rook ) + && ( to > I4 || from > I4 ) ) { MOVE_CURR |= FLAG_PROMO; } + } + else { + if ( IsDiscoverBK( from, to ) ) { return 0; } + + pc = BOARD[from]; + pc_cap = -BOARD[to]; + MOVE_CURR = ( To2Move(to) | From2Move(from) | Piece2Move(pc) + | Cap2Move(pc_cap) ); + if ( ( pc == bishop || pc == rook ) + && ( to < A6 || from < A6 ) ) { MOVE_CURR |= FLAG_PROMO; } + } + + MakeMove( turn, MOVE_CURR, ply ); + if ( I2From(MOVE_LAST) < nsquare ) + { + if ( InCheck(turn) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + return 0; + } + flag = 1; + } + else { + assert( ! InCheck(turn) ); + flag = 2; + } + + ptree->move_last[ply] = ptree->move_last[ply-1]; + value = mate3_and( ptree, Flip(turn), ply+1, flag ); + + UnMakeMove( turn, MOVE_CURR, ply ); + + return value; +} + + +static int +gen_next_evasion_mate( tree_t * restrict ptree, const char *psq, int ply, + int turn, int flag ) +{ + switch ( ptree->anext_move[ply].next_phase ) + { + case mate_king_cap_checker: + ptree->anext_move[ply].next_phase = mate_cap_checker_gen; + MOVE_CURR = gen_king_cap_checker( ptree, psq[0], turn ); + if ( MOVE_CURR ) { return 1; } + + case mate_cap_checker_gen: + ptree->anext_move[ply].next_phase = mate_cap_checker; + ptree->anext_move[ply].move_last = ptree->move_last[ply-1]; + ptree->move_last[ply] = ptree->move_last[ply-1]; + if ( psq[1] == nsquare ) + { + ptree->move_last[ply] + = gen_move_to( ptree, psq[0], turn, ptree->move_last[ply-1] ); + } + + case mate_cap_checker: + if ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + + case mate_king_cap_gen: + ptree->anext_move[ply].next_phase = mate_king_cap; + ptree->anext_move[ply].move_last = ptree->move_last[ply-1]; + ptree->move_last[ply] + = gen_king_move( ptree, psq, turn, 1, ptree->move_last[ply-1] ); + + case mate_king_cap: + if ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + + case mate_king_move_gen: + ptree->anext_move[ply].next_phase = mate_king_move; + ptree->anext_move[ply].move_last = ptree->move_last[ply-1]; + ptree->move_last[ply] + = gen_king_move( ptree, psq, turn, 0, ptree->move_last[ply-1] ); + + case mate_king_move: + if ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + + case mate_intercept_move_gen: + ptree->anext_move[ply].remaining = 0; + ptree->anext_move[ply].next_phase = mate_intercept_move; + ptree->anext_move[ply].move_last = ptree->move_last[ply-1]; + ptree->move_last[ply] = ptree->move_last[ply-1]; + if ( psq[1] == nsquare && abs(BOARD[(int)psq[0]]) != knight ) + { + int n; + ptree->move_last[ply] = gen_intercept( ptree, psq[0], ply, turn, &n, + ptree->move_last[ply-1], + flag ); + if ( n < 0 ) + { + ptree->anext_move[ply].next_phase = mate_intercept_drop_sup; + ptree->anext_move[ply].remaining = 0; + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + + ptree->anext_move[ply].remaining = n; + } + + case mate_intercept_move: + if ( ptree->anext_move[ply].remaining-- ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + ptree->anext_move[ply].next_phase = mate_intercept_weak_move; + + case mate_intercept_weak_move: + if ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + return 1; + } + break; + + default: + assert( 0 ); + } + + return 0; +} + + +static void +checker( const tree_t * restrict ptree, char *psq, int turn ) +{ + bitboard_t bb, bb_checkers; + int n, sq0, sq1, sq_king; + + if ( turn ) + { + sq_king = SQ_WKING; + bb_checkers = BB_BOCCUPY; + } + else { + sq_king = SQ_BKING; + bb_checkers = BB_WOCCUPY; + } + bb = attacks_to_piece( ptree, sq_king ); + BBAnd( bb, bb, bb_checkers ); + + assert( BBToU(bb) ); + sq0 = LastOne( bb ); + sq1 = nsquare; + + Xor( sq0, bb ); + if ( BBToU( bb ) ) + { + sq1 = LastOne( bb ); + if ( BBContract( abb_king_attacks[sq_king], abb_mask[sq1] ) ) + { + n = sq0; + sq0 = sq1; + sq1 = n; + } + } + + psq[0] = (char)sq0; + psq[1] = (char)sq1; +} + + +static unsigned int +gen_king_cap_checker( const tree_t * restrict ptree, int to, int turn ) +{ + unsigned int move; + int from; + + if ( turn ) + { + from = SQ_WKING; + if ( ! BBContract( abb_king_attacks[from], + abb_mask[to] ) ) { return 0;} + if ( is_white_attacked( ptree, to ) ) { return 0; } + move = Cap2Move(BOARD[to]); + } + else { + from = SQ_BKING; + if ( ! BBContract( abb_king_attacks[from], + abb_mask[to] ) ) { return 0;} + if ( is_black_attacked( ptree, to ) ) { return 0; } + move = Cap2Move(-BOARD[to]); + } + move |= To2Move(to) | From2Move(from) | Piece2Move(king); + + return move; +} + + +static unsigned int * +gen_move_to( const tree_t * restrict ptree, int to, int turn, + unsigned int * restrict pmove ) +{ + bitboard_t bb; + int direc, from, pc, flag_promo, flag_unpromo; + + bb = attacks_to_piece( ptree, to ); + if ( turn ) + { + BBAnd( bb, bb, BB_WOCCUPY ); + BBNotAnd( bb, abb_mask[SQ_WKING] ); + while ( BBToU(bb) ) + { + from = LastOne( bb ); + Xor( from, bb ); + + direc = (int)adirec[SQ_WKING][from]; + if ( direc && is_pinned_on_white_king( ptree, from, direc ) ) + { + continue; + } + + flag_promo = 0; + flag_unpromo = 1; + pc = -BOARD[from]; + switch ( pc ) + { + case pawn: + if ( to > I4 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + case lance: case knight: + if ( to > I3 ) { flag_promo = 1; flag_unpromo = 0; } + else if ( to > I4 ) { flag_promo = 1; } + break; + + case silver: + if ( to > I4 || from > I4 ) { flag_promo = 1; } + break; + + case bishop: case rook: + if ( to > I4 + || from > I4 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + default: + break; + } + assert( flag_promo || flag_unpromo ); + if ( flag_promo ) + { + *pmove++ = ( From2Move(from) | To2Move(to) | FLAG_PROMO + | Piece2Move(pc) | Cap2Move(BOARD[to]) ); + } + if ( flag_unpromo ) + { + *pmove++ = ( From2Move(from) | To2Move(to) + | Piece2Move(pc) | Cap2Move(BOARD[to]) ); + } + } + } + else { + BBAnd( bb, bb, BB_BOCCUPY ); + BBNotAnd( bb, abb_mask[SQ_BKING] ); + while ( BBToU(bb) ) + { + from = FirstOne( bb ); + Xor( from, bb ); + + direc = (int)adirec[SQ_BKING][from]; + if ( direc && is_pinned_on_black_king( ptree, from, direc ) ) + { + continue; + } + + flag_promo = 0; + flag_unpromo = 1; + pc = BOARD[from]; + switch ( pc ) + { + case pawn: + if ( to < A6 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + case lance: case knight: + if ( to < A7 ) { flag_promo = 1; flag_unpromo = 0; } + else if ( to < A6 ) { flag_promo = 1; } + break; + + case silver: + if ( to < A6 || from < A6 ) { flag_promo = 1; } + break; + + case bishop: case rook: + if ( to < A6 + || from < A6 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + default: + break; + } + assert( flag_promo || flag_unpromo ); + if ( flag_promo ) + { + *pmove++ = ( From2Move(from) | To2Move(to) | FLAG_PROMO + | Piece2Move(pc) | Cap2Move(-BOARD[to]) ); + } + if ( flag_unpromo ) + { + *pmove++ = ( From2Move(from) | To2Move(to) + | Piece2Move(pc) | Cap2Move(-BOARD[to]) ); + } + } + } + + return pmove; +} + + +static unsigned int * +gen_king_move( const tree_t * restrict ptree, const char *psq, int turn, + int is_capture, unsigned int * restrict pmove ) +{ + bitboard_t bb; + int to, from; + + if ( turn ) + { + from = SQ_WKING; + bb = abb_king_attacks[from]; + if ( is_capture ) + { + BBAnd( bb, bb, BB_BOCCUPY ); + BBNotAnd( bb, abb_mask[(int)psq[0]] ); + } + else { BBNotAnd( bb, BB_BOCCUPY ); } + BBNotAnd( bb, BB_WOCCUPY ); + } + else { + from = SQ_BKING; + bb = abb_king_attacks[from]; + if ( is_capture ) + { + BBAnd( bb, bb, BB_WOCCUPY ); + BBNotAnd( bb, abb_mask[(int)psq[0]] ); + } + else { BBNotAnd( bb, BB_WOCCUPY ); } + BBNotAnd( bb, BB_BOCCUPY ); + } + + while ( BBToU(bb) ) + { + to = LastOne( bb ); + Xor( to, bb ); + + if ( psq[1] != nsquare + && ( adirec[from][(int)psq[1]] + == adirec[from][to] ) ) { continue; } + + if ( psq[0] != to + && adirec[from][(int)psq[0]] == adirec[from][to] ) { + if ( adirec[from][(int)psq[0]] & flag_cross ) + { + if ( abs(BOARD[(int)psq[0]]) == lance + || abs(BOARD[(int)psq[0]]) == rook + || abs(BOARD[(int)psq[0]]) == dragon ) { continue; } + } + else if ( ( adirec[from][(int)psq[0]] & flag_diag ) + && ( abs(BOARD[(int)psq[0]]) == bishop + || abs(BOARD[(int)psq[0]]) == horse ) ){ continue; } + } + + if ( turn ) + { + if ( is_white_attacked( ptree, to ) ) { continue; } + + *pmove++ = ( From2Move(from) | To2Move(to) + | Piece2Move(king) | Cap2Move(BOARD[to]) ); + } + else { + if ( is_black_attacked( ptree, to ) ) { continue; } + + *pmove++ = ( From2Move(from) | To2Move(to) + | Piece2Move(king) | Cap2Move(-BOARD[to]) ); + } + } + + return pmove; +} + + +static unsigned int * +gen_intercept( tree_t * restrict __ptree__, int sq_checker, int ply, int turn, + int * restrict premaining, unsigned int * restrict pmove, + int flag ) +{ +#define Drop(pc) ( To2Move(to) | Drop2Move(pc) ) + + const tree_t * restrict ptree = __ptree__; + bitboard_t bb_atk, bb_defender, bb; + unsigned int amove[16]; + unsigned int hand; + int n0, n1, inc, pc, sq_k, to, from, direc, nmove, nsup, i, min_chuai, itemp; + int dist, flag_promo, flag_unpromo; + + n0 = n1 = 0; + if ( turn ) + { + sq_k = SQ_WKING; + bb_defender = BB_WOCCUPY; + BBNotAnd( bb_defender, abb_mask[sq_k] ); + } + else { + sq_k = SQ_BKING; + bb_defender = BB_BOCCUPY; + BBNotAnd( bb_defender, abb_mask[sq_k] ); + } + + switch ( adirec[sq_k][sq_checker] ) + { + case direc_rank: + min_chuai = ( sq_k < A8 || I2 < sq_k ) ? 2 : 4; + inc = 1; + break; + + case direc_diag1: + min_chuai = 3; + inc = 8; + break; + + case direc_file: + itemp = (int)aifile[sq_k]; + min_chuai = ( itemp == file1 || itemp == file9 ) ? 2 : 4; + inc = 9; + break; + + default: + assert( (int)adirec[sq_k][sq_checker] == direc_diag2 ); + min_chuai = 3; + inc = 10; + } + if ( sq_k > sq_checker ) { inc = -inc; } + + for ( dist = 1, to = sq_k + inc; + to != sq_checker; + dist += 1, to += inc ) { + + assert( 0 <= to && to < nsquare && BOARD[to] == empty ); + + nmove = 0; + bb_atk = attacks_to_piece( ptree, to ); + BBAnd( bb, bb_defender, bb_atk ); + while ( BBToU(bb) ) + { + from = LastOne( bb ); + Xor( from, bb ); + + direc = (int)adirec[sq_k][from]; + flag_promo = 0; + flag_unpromo = 1; + if ( turn ) + { + if ( direc && is_pinned_on_white_king( ptree, from, direc ) ) + { + continue; + } + pc = -BOARD[from]; + switch ( pc ) + { + case pawn: + if ( to > I4 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + case lance: case knight: + if ( to > I3 ) { flag_promo = 1; flag_unpromo = 0; } + else if ( to > I4 ) { flag_promo = 1; } + break; + + case silver: + if ( to > I4 || from > I4 ) { flag_promo = 1; } + break; + + case bishop: case rook: + if ( to > I4 + || from > I4 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + default: + break; + } + } + else { + if ( direc && is_pinned_on_black_king( ptree, from, direc ) ) + { + continue; + } + pc = BOARD[from]; + switch ( pc ) + { + case pawn: + if ( to < A6 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + case lance: case knight: + if ( to < A7 ) { flag_promo = 1; flag_unpromo = 0; } + else if ( to < A6 ) { flag_promo = 1; } + break; + + case silver: + if ( to < A6 || from < A6 ) { flag_promo = 1; } + break; + + case bishop: case rook: + if ( to < A6 + || from < A6 ) { flag_promo = 1; flag_unpromo = 0; } + break; + + default: + break; + } + } + assert( flag_promo || flag_unpromo ); + if ( flag_promo ) + { + amove[nmove++] = ( From2Move(from) | To2Move(to) + | FLAG_PROMO | Piece2Move(pc) ); + } + if ( flag_unpromo ) + { + amove[nmove++] = ( From2Move(from) | To2Move(to) + | Piece2Move(pc) ); + } + } + + nsup = ( to == sq_k + inc ) ? nmove + 1 : nmove; + if ( nsup > 1 ) + { + for ( i = n0 + n1 - 1; i >= n0; i-- ) { pmove[i+nmove] = pmove[i]; } + for ( i = 0; i < nmove; i++ ) { pmove[n0++] = amove[i]; } + } + else if ( nmove ) { pmove[n0 + n1++] = amove[0]; } + + if ( ! nsup ) + { + /* - tentative assumption - */ + /* no recursive drops at non-supported square. */ + if ( flag == 2 ) { continue; } + + /* -tentative assumption- */ + /* no intercept-drop at non-supported square. */ + if ( I2To(MOVE_LAST) == sq_checker && dist > min_chuai ) { continue; } + } + + nmove = 0; + + if ( turn ) { + + hand = HAND_W; + + if ( nsup ) { + + if ( IsHandRook(hand) ) { amove[nmove++] = Drop(rook); } + else if ( IsHandLance(hand) && to < A1 ) + { + amove[nmove++] = Drop(lance); + } + else if ( IsHandPawn(hand) + && to < A1 + && ! ( BBToU(BB_WPAWN) & ( mask_file1 >> aifile[to] ) ) + && ! IsMateWPawnDrop( __ptree__, to ) ) + { + amove[nmove++] = Drop(pawn); + } + + } else { + + if ( IsHandPawn(hand) + && to < A1 + && ! ( BBToU(BB_WPAWN) & ( mask_file1 >> aifile[to] ) ) + && ! IsMateWPawnDrop( __ptree__, to ) ) + { + amove[nmove++] = Drop(pawn); + } + if ( IsHandLance(hand) && to < A1 ) { amove[nmove++] = Drop(lance); } + if ( IsHandRook(hand) ) { amove[nmove++] = Drop(rook); } + } + + if ( IsHandKnight(hand) && to < A2 ) { amove[nmove++] = Drop(knight); } + + } else { + + hand = HAND_B; + + if ( nsup ) { + + if ( IsHandRook(hand) ) { amove[nmove++] = Drop(rook); } + else if ( IsHandLance(hand) && to > I9 ) + { + amove[nmove++] = Drop(lance); + } + else if ( IsHandPawn(hand) + && to > I9 + && ! ( BBToU(BB_BPAWN) & ( mask_file1 >> aifile[to] ) ) + && ! IsMateBPawnDrop( __ptree__, to ) ) + { + amove[nmove++] = Drop(pawn); + } + + } else { + + if ( IsHandPawn(hand) + && to > I9 + && ! ( BBToU(BB_BPAWN) & ( mask_file1 >> aifile[to] ) ) + && ! IsMateBPawnDrop( __ptree__, to ) ) + { + amove[nmove++] = Drop(pawn); + } + if ( IsHandLance(hand) && to > I9 ) { amove[nmove++] = Drop(lance); } + if ( IsHandRook(hand) ) { amove[nmove++] = Drop(rook); } + } + + if ( IsHandKnight(hand) && to > I8 ) { amove[nmove++] = Drop(knight); } + } + + if ( IsHandSilver(hand) ) { amove[nmove++] = Drop(silver); } + if ( IsHandGold(hand) ) { amove[nmove++] = Drop(gold); } + if ( IsHandBishop(hand) ) { amove[nmove++] = Drop(bishop); } + + if ( nsup ) + { + /* - tentative assumption - */ + /* a supported intercepter saves the king for two plies at least. */ + if ( nmove && flag == 0 && dist > min_chuai + && I2From(MOVE_LAST) >= nsquare ) + { + *premaining = -1; + pmove[0] = amove[0]; + return pmove + 1; + } + + for ( i = n0 + n1 - 1; i >= n0; i-- ) { pmove[i+nmove] = pmove[i]; } + for ( i = 0; i < nmove; i++ ) { pmove[n0++] = amove[i]; } + } + else for ( i = 0; i < nmove; i++ ) { pmove[n0 + n1++] = amove[i]; } + } + + *premaining = n0; + return pmove + n0 + n1; + +#undef Drop +} diff --git a/movgenex.c b/movgenex.c new file mode 100644 index 0000000..f21d240 --- /dev/null +++ b/movgenex.c @@ -0,0 +1,415 @@ +#include +#include +#include "shogi.h" + + +#define BAddMoveCap(piece) \ + utemp = From2Move(from) | Piece2Move(piece); \ + while ( BBToU( bb_move ) ) { \ + to = LastOne( bb_move ); \ + *pmove++ = To2Move(to) | Cap2Move(-BOARD[to]) | utemp; \ + Xor( to, bb_move ); } + +#define BAddMove(piece) utemp = From2Move(from) | Piece2Move(piece); \ + while ( BBToU( bb_move ) ) { \ + to = LastOne( bb_move ); \ + *pmove++ = To2Move(to) | utemp; \ + Xor( to, bb_move ); } + + +#define WAddMoveCap(piece) \ + utemp = From2Move(from) | Piece2Move(piece); \ + while ( BBToU( bb_move ) ) { \ + to = FirstOne( bb_move ); \ + *pmove++ = To2Move(to) | Cap2Move(BOARD[to]) | utemp; \ + Xor( to, bb_move ); } + +#define WAddMove(piece) utemp = From2Move(from) | Piece2Move(piece); \ + while ( BBToU( bb_move ) ) { \ + to = FirstOne( bb_move ); \ + *pmove++ = To2Move(to) | utemp; \ + Xor( to, bb_move ); } + + +unsigned int * +b_gen_cap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + int from, to; + unsigned int utemp, ubb_piece0, ubb_piece1, ubb_piece2, ubb_move0; + bitboard_t bb_target, bb_move, bb_piece; + + bb_target = BB_WOCCUPY; + + ubb_move0 = BB_BPAWN_ATK.p[0] & bb_target.p[0] & 0x003ffffU; + while( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + from = to + 9; + *pmove++ = To2Move(to) | From2Move(from) + | Cap2Move(-BOARD[to]) | Piece2Move(pawn); + ubb_move0 ^= abb_mask[to].p[0]; + } + + ubb_piece1 = BB_BBISHOP.p[1]; + ubb_piece2 = BB_BBISHOP.p[2]; + while( ubb_piece1 | ubb_piece2 ) + { + from = last_one12( ubb_piece1, ubb_piece2 ); + ubb_move0 = BishopAttack0(from) & bb_target.p[0]; + utemp = From2Move(from) | Piece2Move(bishop); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | Cap2Move(-BOARD[to]) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + ubb_piece1 ^= abb_mask[from].p[1]; + ubb_piece2 ^= abb_mask[from].p[2]; + } + ubb_piece0 = BB_BBISHOP.p[0]; + while( ubb_piece0 ) + { + from = last_one0( ubb_piece0 ); + AttackBishop( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + BAddMoveCap( bishop ); + ubb_piece0 ^= abb_mask[from].p[0]; + } + + ubb_piece1 = BB_BROOK.p[1]; + ubb_piece2 = BB_BROOK.p[2]; + while( ubb_piece1 | ubb_piece2 ) + { + from = last_one12( ubb_piece1, ubb_piece2 ); + AttackRook( bb_move, from ); + ubb_move0 = bb_move.p[0] & bb_target.p[0]; + utemp = From2Move(from) | Piece2Move(rook); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | Cap2Move(-BOARD[to]) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + ubb_piece1 ^= abb_mask[from].p[1]; + ubb_piece2 ^= abb_mask[from].p[2]; + } + ubb_piece0 = BB_BROOK.p[0]; + while( ubb_piece0 ) + { + from = last_one0( ubb_piece0 ); + AttackRook( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + BAddMoveCap( rook ); + ubb_piece0 ^= abb_mask[from].p[0]; + } + + bb_piece = BB_BLANCE; + bb_target.p[0] &= 0x3fe00; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + ubb_move0 = AttackFile(from).p[0] + & abb_minus_rays[from].p[0] & bb_target.p[0]; + utemp = From2Move(from) | Piece2Move(lance); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | Cap2Move(-BOARD[to]) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + Xor( from, bb_piece ); + } + + return pmove; +} + + +unsigned int * +b_gen_nocap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + bitboard_t bb_target, bb_move, bb_piece; + unsigned int ubb_piece0, ubb_piece1, ubb_piece2, ubb_move0, ubb_target0; + unsigned int utemp; + int to, from; + + BBOr( bb_target, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_target, bb_target ); + + ubb_move0 = BB_BPAWN_ATK.p[0] & bb_target.p[0] & 0x003ffffU; + while( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + from = to + 9; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(pawn); + ubb_move0 ^= abb_mask[to].p[0]; + } + + ubb_piece1 = BB_BBISHOP.p[1]; + ubb_piece2 = BB_BBISHOP.p[2]; + while( ubb_piece1 | ubb_piece2 ) + { + from = last_one12( ubb_piece1, ubb_piece2 ); + ubb_move0 = BishopAttack0(from) & bb_target.p[0]; + utemp = From2Move(from) | Piece2Move(bishop); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + ubb_piece1 ^= abb_mask[from].p[1]; + ubb_piece2 ^= abb_mask[from].p[2]; + } + ubb_piece0 = BB_BBISHOP.p[0]; + while( ubb_piece0 ) + { + from = last_one0( ubb_piece0 ); + AttackBishop( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + BAddMove( bishop ); + ubb_piece0 ^= abb_mask[from].p[0]; + } + + ubb_piece1 = BB_BROOK.p[1]; + ubb_piece2 = BB_BROOK.p[2]; + while( ubb_piece1 | ubb_piece2 ) + { + from = last_one12( ubb_piece1, ubb_piece2 ); + AttackRook( bb_move, from ); + ubb_move0 = bb_move.p[0] & bb_target.p[0]; + utemp = From2Move(from) | Piece2Move(rook); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + ubb_piece1 ^= abb_mask[from].p[1]; + ubb_piece2 ^= abb_mask[from].p[2]; + } + ubb_piece0 = BB_BROOK.p[0]; + while( ubb_piece0 ) + { + from = last_one0( ubb_piece0 ); + AttackRook( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + BAddMove( rook ); + ubb_piece0 ^= abb_mask[from].p[0]; + } + + bb_piece = BB_BLANCE; + ubb_target0 = bb_target.p[0] & 0x3fe00; + while( BBToU( bb_piece ) ) + { + from = LastOne( bb_piece ); + ubb_move0 = AttackFile(from).p[0] + & abb_minus_rays[from].p[0] & ubb_target0; + utemp = From2Move(from) | Piece2Move(lance); + while ( ubb_move0 ) + { + to = last_one0( ubb_move0 ); + *pmove++ = To2Move(to) | utemp; + ubb_move0 ^= abb_mask[to].p[0]; + } + Xor( from, bb_piece ); + } + + return pmove; +} + + +unsigned int * +w_gen_cap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + bitboard_t bb_target, bb_move, bb_piece; + unsigned int utemp, ubb_piece0, ubb_piece1, ubb_piece2, ubb_move2; + int from, to; + + bb_target = BB_BOCCUPY; + + ubb_move2 = BB_WPAWN_ATK.p[2] & bb_target.p[2] & 0x7fffe00U; + while( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + from = to - 9; + *pmove++ = To2Move(to) | From2Move(from) + | Cap2Move(BOARD[to]) | Piece2Move(pawn); + ubb_move2 ^= abb_mask[to].p[2]; + } + + ubb_piece0 = BB_WBISHOP.p[0]; + ubb_piece1 = BB_WBISHOP.p[1]; + while( ubb_piece0 | ubb_piece1 ) + { + from = first_one01( ubb_piece0, ubb_piece1 ); + ubb_move2 = BishopAttack2(from) & bb_target.p[2]; + utemp = From2Move(from) | Piece2Move(bishop); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | Cap2Move(BOARD[to]) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + ubb_piece0 ^= abb_mask[from].p[0]; + ubb_piece1 ^= abb_mask[from].p[1]; + } + ubb_piece2 = BB_WBISHOP.p[2]; + while( ubb_piece2 ) + { + from = first_one2( ubb_piece2 ); + AttackBishop( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + WAddMoveCap( bishop ); + ubb_piece2 ^= abb_mask[from].p[2]; + } + + ubb_piece0 = BB_WROOK.p[0]; + ubb_piece1 = BB_WROOK.p[1]; + while( ubb_piece0 | ubb_piece1 ) + { + from = first_one01( ubb_piece0, ubb_piece1 ); + AttackRook( bb_move, from ); + ubb_move2 = bb_move.p[2] & bb_target.p[2]; + utemp = From2Move(from) | Piece2Move(rook); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | Cap2Move(BOARD[to]) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + ubb_piece0 ^= abb_mask[from].p[0]; + ubb_piece1 ^= abb_mask[from].p[1]; + } + ubb_piece2 = BB_WROOK.p[2]; + while( ubb_piece2 ) + { + from = first_one2( ubb_piece2 ); + AttackRook( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + WAddMoveCap( rook ); + ubb_piece2 ^= abb_mask[from].p[2]; + } + + bb_piece = BB_WLANCE; + bb_target.p[2] &= 0x3fe00; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + ubb_move2 = AttackFile(from).p[2] + & abb_plus_rays[from].p[2] & bb_target.p[2]; + utemp = From2Move(from) | Piece2Move(lance); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | Cap2Move(BOARD[to]) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + Xor( from, bb_piece ); + } + + return pmove; +} + + +unsigned int * +w_gen_nocap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ) +{ + bitboard_t bb_target, bb_piece, bb_move; + unsigned int ubb_piece0, ubb_piece1, ubb_piece2, ubb_move2, ubb_target2; + unsigned int utemp; + int to, from; + + BBOr( bb_target, BB_BOCCUPY, BB_WOCCUPY ); + BBNot( bb_target, bb_target ); + + ubb_move2 = BB_WPAWN_ATK.p[2] & bb_target.p[2] & 0x7fffe00U;; + while( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + from = to - 9; + *pmove++ = To2Move(to) | From2Move(from) | Piece2Move(pawn); + ubb_move2 ^= abb_mask[to].p[2]; + } + + ubb_piece0 = BB_WBISHOP.p[0]; + ubb_piece1 = BB_WBISHOP.p[1]; + while( ubb_piece0 | ubb_piece1 ) + { + from = first_one01( ubb_piece0, ubb_piece1 ); + ubb_move2 = BishopAttack2(from) & bb_target.p[2]; + utemp = From2Move(from) | Piece2Move(bishop); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + ubb_piece0 ^= abb_mask[from].p[0]; + ubb_piece1 ^= abb_mask[from].p[1]; + } + ubb_piece2 = BB_WBISHOP.p[2]; + while( ubb_piece2 ) + { + from = first_one2( ubb_piece2 ); + AttackBishop( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + WAddMove( bishop ); + ubb_piece2 ^= abb_mask[from].p[2]; + } + + ubb_piece0 = BB_WROOK.p[0]; + ubb_piece1 = BB_WROOK.p[1]; + while( ubb_piece0 | ubb_piece1 ) + { + from = first_one01( ubb_piece0, ubb_piece1 ); + AttackRook( bb_move, from ); + ubb_move2 = bb_move.p[2] & bb_target.p[2]; + utemp = From2Move(from) | Piece2Move(rook); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + ubb_piece0 ^= abb_mask[from].p[0]; + ubb_piece1 ^= abb_mask[from].p[1]; + } + ubb_piece2 = BB_WROOK.p[2]; + while( ubb_piece2 ) + { + from = first_one2( ubb_piece2 ); + AttackRook( bb_move, from ); + BBAnd( bb_move, bb_move, bb_target ); + WAddMove( rook ); + ubb_piece2 ^= abb_mask[from].p[2]; + } + + bb_piece = BB_WLANCE; + ubb_target2 = bb_target.p[2] & 0x3fe00; + while( BBToU( bb_piece ) ) + { + from = FirstOne( bb_piece ); + ubb_move2 = AttackFile(from).p[2] + & abb_plus_rays[from].p[2] & ubb_target2; + utemp = From2Move(from) | Piece2Move(lance); + while ( ubb_move2 ) + { + to = first_one2( ubb_move2 ); + *pmove++ = To2Move(to) | utemp; + ubb_move2 ^= abb_mask[to].p[2]; + } + Xor( from, bb_piece ); + } + + return pmove; +} + + +#undef BAddMoveCap +#undef BAddMove +#undef WAddMoveCap +#undef WAddMove diff --git a/next.c b/next.c new file mode 100644 index 0000000..f0153ac --- /dev/null +++ b/next.c @@ -0,0 +1,355 @@ +#include +#include +#include +#include "shogi.h" + +int +gen_next_move( tree_t * restrict ptree, int ply, int turn ) +{ + switch ( ptree->anext_move[ply].next_phase ) + { + case next_move_hash: + { + unsigned int * restrict pmove; + int * restrict psortv = ptree->sort_value; + unsigned int move, killer1, killer2, move_hash, move_best, move_second; + int i, j, sortv, n, value_best, value_second, value, remaining; + + ptree->anext_move[ply].phase_done = 0; + ptree->anext_move[ply].next_phase = next_move_capture; + ptree->anext_move[ply].move_last = pmove = ptree->move_last[ply]; + ptree->move_last[ply] = GenCaptures( turn, pmove ); + + move_hash = ptree->amove_hash[ply]; + killer1 = ptree->amove_killer[ply].no1; + killer2 = ptree->amove_killer[ply].no2; + remaining = 0; + move_best = move_second = 0; + value_best = value_second = 0; + n = (int)( ptree->move_last[ply] - pmove ); + for ( i = 0; i < n; i++ ) + { + move = pmove[i]; + sortv = swap( ptree, move, -1, INT_MAX, turn ); + if ( sortv > value_best ) + { + move_second = move_best; + value_second = value_best; + value_best = sortv; + move_best = move; + } + else if ( sortv > value_second ) + { + move_second = move; + value_second = sortv; + } + if ( move == move_hash ) { sortv = INT_MIN; } + else if ( UToFromToPromo(move) == killer1 ) + { + killer1 = 0; + value = ptree->amove_killer[ply].no1_value + + p_value_ex[15U+UToCap(move)]; + if ( sortv < value ) { sortv = value; } + if ( sortv > -1 ) { remaining++; } + } + else if ( UToFromToPromo(move) == killer2 ) + { + killer2 = 0; + value = ptree->amove_killer[ply].no2_value + + p_value_ex[15U+UToCap(move)]; + if ( sortv < value ) { sortv = value; } + if ( sortv > -1 ) { remaining++; } + } + else if ( sortv > -1 ) { remaining++; } + psortv[i] = sortv; + } + + if ( killer1 + && killer1 != move_hash + && ptree->amove_killer[ply].no1_value > -1 + && is_move_valid( ptree, killer1, turn ) ) + { + *( ptree->move_last[ply]++ ) = killer1; + psortv[n++] = ptree->amove_killer[ply].no1_value; + remaining++; + } + + if ( killer2 + && killer2 != move_hash + && ptree->amove_killer[ply].no2_value > -1 + && is_move_valid( ptree, killer2, turn ) ) + { + *( ptree->move_last[ply]++ ) = killer2; + psortv[n++] = ptree->amove_killer[ply].no2_value; + remaining++; + } + + ptree->anext_move[ply].value_cap1 = value_best; + ptree->anext_move[ply].move_cap1 = move_best; + ptree->anext_move[ply].value_cap2 = value_second; + ptree->anext_move[ply].move_cap2 = move_second; + ptree->anext_move[ply].remaining = remaining; + + /* insertion sort */ + psortv[n] = INT_MIN; + for ( i = n-2; i >= 0; i-- ) + { + sortv = psortv[i]; move = pmove[i]; + for ( j = i+1; psortv[j] > sortv; j++ ) + { + psortv[j-1] = psortv[j]; pmove[j-1] = pmove[j]; + } + psortv[j-1] = sortv; pmove[j-1] = move; + } + if ( psortv[n-1] == INT_MIN ) { ptree->move_last[ply]--; } + +#if ! defined(MINIMUM) + if ( move_hash && ! is_move_valid( ptree, move_hash, turn ) ) + { + out_warning( "An invalid hash move is found!!" ); + ptree->amove_hash[ply] = move_hash = 0U; + } +#endif + + if ( move_hash ) + { + if ( move_hash == move_best ) + { + ptree->anext_move[ply].phase_done |= phase_cap1; + } + + if ( UToFromToPromo(move_hash) == killer1 ) + { + ptree->anext_move[ply].phase_done |= phase_killer1; + } + else if ( UToFromToPromo(move_hash) == killer2 ) + { + ptree->anext_move[ply].phase_done |= phase_killer2; + } + ptree->anext_move[ply].phase_done |= phase_hash; + MOVE_CURR = move_hash; + return 1; + } + } + + case next_move_capture: + if ( ptree->anext_move[ply].remaining-- ) + { + unsigned int move; + + MOVE_CURR = move = *(ptree->anext_move[ply].move_last++); + if ( move == ptree->anext_move[ply].move_cap1 ) + { + ptree->anext_move[ply].phase_done |= phase_cap1; + } + + if ( UToFromToPromo(move) == ptree->amove_killer[ply].no1 ) + { + ptree->anext_move[ply].phase_done |= phase_killer1; + } + else if ( UToFromToPromo(move) == ptree->amove_killer[ply].no2 ) + { + ptree->anext_move[ply].phase_done |= phase_killer2; + } + return 1; + } + + { + unsigned int * restrict pmove; + unsigned int value_best, value, key, good, tried; + int i, n, ibest; + + value_best = 0; + ibest = -1; + ptree->move_last[ply] = GenNoCaptures( turn, ptree->move_last[ply] ); + ptree->move_last[ply] = GenDrop( turn, ptree->move_last[ply] ); + n = (int)( ptree->move_last[ply] - ptree->anext_move[ply].move_last ); + pmove = ptree->anext_move[ply].move_last; + for ( i = 0; i < n; i++ ) + { + if ( pmove[i] == ptree->amove_hash[ply] + || ( pmove[i] == ptree->amove_killer[ply].no1 + && ( ptree->anext_move[ply].phase_done + & phase_killer1 ) ) + || ( pmove[i] == ptree->amove_killer[ply].no2 + && ( ptree->anext_move[ply].phase_done + & phase_killer2 ) ) ) + { + pmove[i] = 0; + continue; + } + + if ( UToCap(pmove[i]) ) { continue; } + if ( I2IsPromote(pmove[i]) + && I2PieceMove(pmove[i]) != silver ) { continue; } + + + key = phash( pmove[i], turn ); + good = ptree->hist_good[key] + 1; + tried = ptree->hist_tried[key] + 2; + value = ( good * 8192U ) / tried; + if ( value > value_best ) + { + value_best = value; + ibest = i; + } + } + + if ( ibest >= 0 ) + { + ptree->anext_move[ply].phase_done |= phase_history1; + ptree->anext_move[ply].next_phase = next_move_history2; + MOVE_CURR = pmove[ibest]; + pmove[ibest] = 0; + return 1; + } + } + + case next_move_history2: + { + unsigned int * restrict pmove; + unsigned int value_best, value, key, good, tried; + int ibest, i, n; + + ptree->anext_move[ply].next_phase = next_move_misc; + value_best = 0; + ibest = -1; + n = (int)( ptree->move_last[ply] - ptree->anext_move[ply].move_last ); + pmove = ptree->anext_move[ply].move_last; + + for ( i = 0; i < n; i++ ) + { + if ( UToCap(pmove[i]) ) { continue; } + if ( I2IsPromote(pmove[i]) + && I2PieceMove(pmove[i]) != silver ) { continue; } + + key = phash( pmove[i], turn ); + good = ptree->hist_good[key] + 1; + tried = ptree->hist_tried[key] + 2; + value = ( good * 8192U ) / tried; + if ( value > value_best && pmove[i] ) + { + value_best = value; + ibest = i; + } + } + + if ( ibest >= 0 ) + { + ptree->anext_move[ply].phase_done |= phase_history2; + MOVE_CURR = pmove[ibest]; + pmove[ibest] = 0; + return 1; + } + } + + default: + assert( ptree->anext_move[ply].next_phase == next_move_misc ); + while ( ptree->anext_move[ply].move_last < ptree->move_last[ply] ) + { + if ( *( ptree->anext_move[ply].move_last ) ) + { + MOVE_CURR = *(ptree->anext_move[ply].move_last++); + ptree->anext_move[ply].phase_done |= phase_misc; + return 1; + } + ptree->anext_move[ply].move_last++; + } + } + return 0; +} + + +int +gen_next_evasion( tree_t * restrict ptree, int ply, int turn ) +{ + switch ( ptree->anext_move[ply].next_phase ) + { + case next_evasion_hash: + ptree->move_last[ply] = GenEvasion( turn, ptree->move_last[ply] ); + + if ( ptree->amove_hash[ply] ) + { +#if ! defined(MINIMUM) + unsigned int * restrict p; + + for ( p = ptree->move_last[ply-1]; p < ptree->move_last[ply]; p++ ) + if ( *p == ptree->amove_hash[ply] ) { break; } + + if ( *p != ptree->amove_hash[ply] ) + { + out_warning( "An invalid hash evasion-move is found!!" ); + out_board( ptree, stdout, 0, 0 ); + Out( "%c%s\n", ach_turn[turn], + str_CSA_move(ptree->amove_hash[ply]) ); + Out( "hash key = %" PRIu64 ", hand = %u, turn = %d\n", + HASH_KEY, HAND_B, turn ); + ptree->amove_hash[ply] = 0U; + } + else +#endif + { + ptree->anext_move[ply].next_phase = next_evasion_genall; + ptree->current_move[ply] = ptree->amove_hash[ply]; + return 1; + } + } + + case next_evasion_genall: + { + unsigned int * restrict pmove; + int * restrict psortv; + unsigned int move; + int n, i, j, sortv; + + ptree->anext_move[ply].next_phase = next_evasion_misc; + ptree->anext_move[ply].move_last = pmove = ptree->move_last[ply-1]; + n = (int)( ptree->move_last[ply] - pmove ); + psortv = ptree->sort_value; + + for ( i = n-1; i >= 0; i-- ) + { + move = pmove[i]; + if ( move == ptree->amove_hash[ply] ) + { + sortv = INT_MIN; + pmove[i] = 0; + } + else if ( I2PieceMove(move) == king ) + { + sortv = p_value_ex[UToCap(move)+15] * 2; + } + else { + sortv = swap( ptree, move, INT_MIN, INT_MAX, turn ); + sortv += estimate_score_diff( ptree, move, turn ); + } + psortv[i] = sortv; + } + + /* insertion sort */ + psortv[n] = INT_MIN; + for ( i = n-2; i >= 0; i-- ) + { + sortv = psortv[i]; move = pmove[i]; + for ( j = i+1; psortv[j] > sortv; j++ ) + { + psortv[j-1] = psortv[j]; pmove[j-1] = pmove[j]; + } + psortv[j-1] = sortv; pmove[j-1] = move; + } + } + + default: + assert( ptree->anext_move[ply].next_phase == next_evasion_misc ); + while ( ptree->anext_move[ply].move_last < ptree->move_last[ply] ) + { + if ( *( ptree->anext_move[ply].move_last ) ) + { + ptree->current_move[ply] = *(ptree->anext_move[ply].move_last++); + return 1; + } + ptree->anext_move[ply].move_last++; + } + } + return 0; +} diff --git a/param.h b/param.h new file mode 100644 index 0000000..e9cecce --- /dev/null +++ b/param.h @@ -0,0 +1,15 @@ +#define DPawn 87 /* 174 */ +#define DLance 235 /* 470 */ +#define DKnight 254 /* 508 */ +#define DProPawn 530 /* 617 */ +#define DProLance 482 /* 717 */ +#define DSilver 371 /* 742 */ +#define DProKnight 500 /* 754 */ +#define DProSilver 489 /* 860 */ +#define DGold 447 /* 894 */ +#define DBishop 571 /* 1142 */ +#define DRook 647 /* 1294 */ +#define DHorse 832 /* 1403 */ +#define DDragon 955 /* 1602 */ +#define DKing 15000 + diff --git a/phash.c b/phash.c new file mode 100644 index 0000000..8592ee5 --- /dev/null +++ b/phash.c @@ -0,0 +1,275 @@ +#include "shogi.h" + +static unsigned short tab[] = { +5866,14081,15663,4534,14969,3034,8274,7570, +14571,15428,2921,37,2534,4727,660,249, +8308,3662,15794,10079,13857,13019,6070,9141, +3942,14629,6891,11561,12156,4793,11132,12989, +10044,16304,9389,2228,5053,3789,5988,4479, +8719,13901,15524,3098,7570,12949,1074,0, +10079,16233,7531,14642,13600,8087,0,11690, +8579,15413,6522,1076,2677,6914,10881,13803, +13806,15789,14642,2135,15427,14023,14079,7708, +3602,0,4976,10910,4821,4543,5455,9368, +4201,9970,10647,15130,10079,15020,5558,15902, +7858,2962,10386,2155,14641,12308,8021,8170, +2490,10528,15427,14629,10528,15136,999,1883, +14912,10788,6360,3677,14960,12257,2294,12614, +8698,12433,9867,12663,16016,11332,8568,8348, +0,3228,8594,11249,6674,3098,9297,10077, +10340,5803,5420,6070,660,5676,1,9497, +8700,14264,5846,12433,16282,12561,12032,13732, +8239,10731,7236,10286,2678,7321,13368,6594, +9047,5291,8293,2809,3268,6485,10509,2277, +10471,595,8887,15919,14413,15881,8827,13726, +13975,14092,7634,1652,8753,6801,6568,15853, +1022,5664,0,9303,15719,6128,6674,7433, +157,9360,6555,12432,9593,9660,14081,13927, +8702,13702,8663,5629,3691,16298,1573,902, +1272,2376,1084,14447,14666,1692,15316,15129, +3833,920,8348,15839,15902,15635,10340,6603, +1764,9634,3833,2470,4704,9646,385,13200, +3009,1562,6846,6598,15861,8924,11762,3766, +9095,431,6599,1987,15902,11064,4972,12175, +14642,13857,6130,5833,13857,12112,8170,9867, +2569,9648,1883,10205,12365,6801,5222,14506, +1819,5142,2172,5291,15542,12112,2092,5560, +4538,5461,15059,11773,15884,2099,6876,11914, +3833,4972,8164,6240,12487,1341,10731,11762, +14548,4471,5044,7576,2392,16129,8175,7559, +12559,15861,6396,2926,7618,8417,15759,0, +9095,3510,7247,1712,16345,2209,8055,0, +319,12209,13144,15159,8238,7822,5014,10077, +2284,7177,6555,15861,14479,2510,8170,11806, +12149,12440,14185,11762,11552,8260,11864,11728, +15046,11561,4707,13420,3615,2233,2277,10668, +2228,8016,1813,2677,5240,6594,6636,9911, +1737,4396,12500,9682,7176,2027,12500,5244, +12112,1768,14571,12663,6729,3662,8170,15641, +15316,9648,5373,5475,6522,14571,3662,15902, +1652,9648,2228,8348,902,5988,9150,13250, +13411,4675,431,5507,10616,13987,15316,1378, +13832,13356,10087,9543,9093,8065,10497,8228, +11762,616,12968,5411,9648,12487,9463,15597, +11292,13985,14482,14600,1705,4678,12020,8828, +6210,9481,14980,5738,5990,14447,1569,8618, +2273,8543,1819,16158,12447,12470,10160,4725, +10778,1412,8040,4300,9550,568,7394,592, +9481,15680,3476,16128,7660,1214,359,6181, +8618,4067,14699,13368,514,11355,8914,3801, +14520,8885,10434,8274,7709,4312,7243,12633, +4867,5981,4538,2490,3034,5558,6801,7512, +14971,4315,4905,11682,8845,3732,1475,11661, +10189,1741,11667,5605,2656,9573,10898,2796, +1817,15216,11152,470,5988,6164,4972,10386, +13128,7247,3268,6729,4300,13101,8568,12663, +13849,3600,9718,11203,10528,13732,5251,14148, +8568,13128,10205,11332,1712,12821,9557,12753, +10505,5276,109,10394,10122,9809,3513,16093, +2490,9943,14798,15084,4598,5174,15648,13109, +0,6971,15426,14500,14571,6831,2623,6801, +6522,15388,3228,11053,0,2902,4285,13853, +14830,5373,15137,10087,3268,2569,6284,2902, +1483,3838,12159,4764,15130,6846,6957,12499, +4604,2739,14629,996,4867,13420,7791,5263, +8119,10088,4128,7950,11875,16244,1147,3473, +3268,15869,12821,11152,9922,1987,8238,15271, +852,6921,4155,7857,11187,2275,14224,15725, +9967,4479,6522,11152,2457,5962,13732,10088, +11226,11359,5053,13420,3727,12487,3823,13412, +13732,5962,11005,11173,8719,4354,6821,11421, +15640,4769,14868,9369,215,8176,2363,1930, +4124,11338,5373,2135,5253,10648,6220,5853, +2741,12020,14070,14571,7476,9011,11415,1306, +5318,13418,7293,15364,8898,3402,11976,9325, +1562,7366,3395,10762,7144,2336,13458,4538, +1159,1987,11652,12964,13513,541,13792,9741, +10840,16129,10917,4534,13600,5462,2357,3890, +6299,11315,13376,13820,13369,4450,14282,3510, +10008,2381,9301,10165,4832,13042,3789,11751, +8021,14286,9341,436,9171,8623,12745,12738, +567,13996,3654,9495,844,8260,15108,2851, +10351,2756,8572,3694,8851,5833,4280,12168, +9550,9095,1652,6594,2534,3662,12009,6827, +7447,2228,6555,15919,2294,6522,6674,0, +13128,15478,3098,14571,1573,13117,16336,3422, +12751,4635,9808,13171,11875,14148,14759,15680, +610,780,14127,15067,12717,11768,2223,4745, +2111,4363,6821,12230,13200,16093,410,15205, +3838,15020,12230,3473,7951,2926,981,13414, +3034,9835,3875,7247,15776,11053,10533,6393, +6729,1712,12233,10088,5152,1378,6043,3871, +6043,12487,15527,11769,12308,5962,11864,10804, +3268,14005,13662,10647,4972,15137,14485,7784, +7275,410,6555,2470,7074,359,12751,12054, +8851,2993,9487,13270,9267,2370,8119,14112, +6639,157,10245,16121,9967,14912,10307,8011, +12094,2470,10322,3570,6154,1453,15084,14801, +3823,7259,780,4972,13047,14500,2569,5833, +56,14601,2690,5036,788,46,2738,9922, +11332,10619,8011,13128,3268,431,13412,8851, +14748,5036,12185,13042,10030,11716,100,16205, +7951,5738,8417,10757,6463,7114,1183,14798, +10205,9721,15359,10074,8727,4587,11875,14615, +10340,9171,9171,4686,7731,4269,13042,8574, +16298,7024,10309,2858,4904,7223,8238,7512, +4556,3532,15222,2161,1162,6476,3221,10290, +11229,15137,14921,1319,4925,1991,7817,7427, +15089,9366,920,3875,4641,11787,14255,4915, +7588,15555,13106,16014,12882,10324,341,1331, +10685,7950,12292,10230,15336,7431,7576,6158, +5058,12149,2777,13434,5263,15769,9877,2704, +10300,867,1443,5285,2155,4474,935,12586, +12601,158,16191,8119,3726,3554,10398,13418, +8170,4867,14642,15478,1712,10136,6674,0, +46,9867,3766,12433,6057,3972,2156,852, +2144,9856,14761,11491,46,363,10561,4484, +999,9557,2902,7323,15084,8274,16087,6679, +6867,6971,2914,586,6240,5988,10386,8304, +3662,10088,5988,12821,7409,8308,15931,14376, +14700,15998,4484,1766,15794,3833,2464,4342, +1358,319,10099,15776,15839,1530,8538,7800, +3641,5569,7469,2104,8314,8976,5962,11857, +10330,8293,12356,1399,1991,5415,10497,15478, +15931,8308,11608,10167,4185,9369,1658,6193, +5962,5132,12322,319,7802,4707,3034,11308, +9559,10788,2228,8339,13513,5222,1712,3098, +1987,2719,1548,15427,13361,13736,5139,1255, +11064,8011,985,6135,788,2761,6522,5362, +13128,3476,6164,12032,2902,15881,11053,14538, +12964,14730,3751,12890,396,1132,6164,7938, +3165,7677,12218,7007,4824,6266,4198,9127, +12821,6463,199,6522,5462,1084,7232,15717, +3354,2788,6294,11857,14174,2235,6135,7808, +15931,10377,1168,9162,7176,3595,10046,1960, +3726,1723,6779,10955,11201,4721,661,7247, +5988,11291,9095,8555,7017,5975,14500,15822, +10887,7999,9648,15908,2938,8293,14629,4233, +8419,5141,12427,9360,2948,9321,13473,1134, +87,4180,2284,8822,3893,15653,15668,12175, +7951,12668,13119,7839,4793,4686,914,9481, +11912,13196,4235,15293,5704,9063,1605,15640, +13270,1192,9967,8698,5576,3059,4067,11809, +5420,7691,16087,764,12487,2273,14224,13412, +5373,12663,788,12433,8870,15902,14019,3966, +1104,11153,13117,8913,46,9922,15240,8260, +9874,1712,9096,7708,4544,15601,0,10529, +7419,9404,653,4067,10717,4901,10009,7229, +9867,7620,1412,14284,9931,8845,13975,8238, +10941,10205,678,11308,14985,12099,3268,11120, +14148,7900,5934,10099,12903,6274,9648,2851, +972,5149,9506,12102,723,15873,14497,5447, +13505,13658,13128,4699,11152,0,0,7808, +2623,6674,9557,7920,10004,14081,0,15478, +12498,7162,13662,14438,6939,10841,6599,7074, +2129,9303,3614,5121,13626,14806,13096,5275, +12032,11431,12265,242,12651,6465,11153,15635, +6154,7864,3263,7708,13216,9466,4929,4763, +16121,12949,6801,14244,6289,3766,8913,3228, +10099,4198,11895,286,13119,10778,12230,7238, +3614,14081,7323,4050,6846,14629,13420,9427, +5460,5130,10528,15705,6369,8361,1212,8866, +12821,15427,7323,2777,9648,8744,5386,8241, +11875,14112,14770,15369,9813,6750,13732,6594, +2464,11682,2031,15129,3532,12112,4831,4354, +2275,10376,11226,11611,8313,9590,14500,8598, +13403,4316,15388,13458,961,208,5995,1225, +6455,5932,11987,2902,13128,1224,1003,7409, +3028,650,13436,8485,6653,911,9425,11421, +9554,10993,15293,514,6670,14578,4972,9874, +8483,2534,14969,1934,14154,8926,11830,8784, +2987,9634,6670,13501,13957,4260,5761,15347, +7991,13414,7159,7233,14206,11665,6715,11079, +3545,5185,15639,14307,9338,8971,15994,5645, +3098,14081,788,8170,10188,8016,5988,15919, +12788,14960,10214,12606,11152,2470,13420,2927, +5240,7370,9648,10099,2039,4846,10610,11823, +13270,14761,5962,7512,10079,4867,8260,2135, +1052,10823,5729,10395,14361,13382,12200,8162, +5117,7531,13361,14520,10602,8685,3654,9777, +16263,6135,6389,11745,10383,8550,12618,10340, +4128,15459,7062,5135,9047,9346,5897,15479, +7268,5576,0,14937,12736,5545,10079,6507, +3238,3204,9865,5843,12487,198,8251,13195, +158,8913,6598,4373,15991,13857,3615,11106, +3244,10654,6394,6183,11633,3098,12257,15427, +1768,7209,11987,963,13857,7247,16046,7239, +13200,11773,2914,12927,11669,13011,1905,9089, +16176,3743,158,5222,12717,5825,16087,9519, +9227,15563,1712,8511,11005,4867,13200,13106, +10788,3606,8680,3447,5833,3709,4526,10847, +2871,6000,2204,15769,2969,7617,15976,7618, +4067,10757,15316,6476,10509,8772,9095,0, +8996,410,14742,7239,10532,9406,7419,15903, +5437,5560,12608,9141,3986,14358,7409,3992, +3221,6170,11821,508,6434,1442,4260,3813, +8308,6456,3534,431,4538,3406,12435,5643, +7144,8870,9186,3775,1817,9905,7951,4336, +2273,12264,6513,2993,16270,5541,5077,6263, +1259,2559,1675,16218,1790,12804,15098,4445, +10336,15216,12144,8618,4573,13679,15000,10189, +2209,4583,5353,13784,2623,7720,5833,11064, +10088,10077,10610,6786,10939,10867,1768,8119, +7394,12056,3789,1074,15413,10527,7247,12487, +8568,10136,2228,13412,4840,1652,6740,11322, +5037,2910,5174,10340,2135,8011,11895,11895, +109,11957,14336,13234,8225,14343,9063,9819, +13517,11635,13549,14936,988,3809,5833,7857, +13462,297,7668,14482,4316,10848,2569,13931, +5593,12056,11103,14170,448,902,1768,11552, +6043,0,0,8646,10391,0,15316,3737, +10594,11026,13280,14667,5713,4057,2787,4191, +3838,7247,8772,14185,15641,8772,0,14081, +0,9429,17,613,7428,15131,15651,14350, +4365,15856,9521,3228,7950,3395,0,6594, +11078,14975,6889,10768,4288,6707,7175,2235, +10896,911,7707,7592,12916,15570,15644,6997, +12523,14757,11895,7109,16279,12969,11690,0, +8854,16304,8568,11334,15641,15478,2275,6193, +7323,5230,9835,12821,1652,12477,5044,359, +12487,13881,10731,8246,14642,14885,6555,15919, +13857,1982,7646,12528,12009,7310,12310,3193, +359,1483,5322,10476,46,3751,3766,6729, +4512,13853,5558,12736,1733,2718,9956,7857, +6519,2187,12964,12949,750,8877,15136,3742, +4495,13851,3811,906,11592,9943,920,10806, +10442,12561,5035,988,15861,8024,10272,16372, +13353,15946,7330,4834,9414,8267,1668,12498, +4530,9162,8648,3883,9451,3900,2028,12427, +11203,3071,5645,10228,11875,8198,4563,215, +3614,5717,11332,1617,11382,13987,12626,11857, +12156,6513,13496,6997,16158,9093,6725,13737, +5890,10008,14447,5475,12215,141,16192,10200, +4450,6688,11090,16109,12256,9027,15355,3422, +10079,14571,13101,9967,12296,3460,8348,15413, +5222,11762,3823,13412,6729,6599,10528,9922, +11152,9171,5362,5373,750,8969,1652,6220, +5373,2569,3742,15119,10340,999,10088,10731, +1028,3228,7369,15256,11822,12904,8121,11641, +11158,5016,10725,8412,1183,9674,11806,12112, +2623,15516,4226,10021,13316,12075,792,4972, +8506,8921,6133,14718,567,4349,13420,5953, +2265,11902,10019,16265,16312,0,9922,3009, +8170,3473,10757,3567,2825,6915,5174,4299, +0,8274,6292,295,6522,1475,359,4538, +5358,5174,3008,6317,12688,4165,752,12052, +10008,3912,7431,13364,9894,13524,284,16013, +9820,11295,1144,2845,3538,10678,13806,1469, +14210,7870,3766,10528,13642,10823,4200,8031, +4671,14093,5541,1214,4115,3286,8217,8018 }; + + +unsigned int phash( unsigned int move, int turn ) +{ + unsigned int a, b; + + move |= ( (unsigned int)turn << 31 ); + move += 0x69fe378e; + move ^= ( move >> 16 ); + move += ( move << 8 ); + move ^= ( move >> 4 ); + b = ( move >> 7 ) & 0x7ff; + a = move >> 18; + + return a ^ tab[b]; +} diff --git a/ponder.c b/ponder.c new file mode 100644 index 0000000..2eb8b7a --- /dev/null +++ b/ponder.c @@ -0,0 +1,106 @@ +#include +#include +#include "shogi.h" + + +int +ponder( tree_t * restrict ptree ) +{ + const char *str; + unsigned int move; + int iret; + + if ( ( game_status & ( mask_game_end | flag_noponder | flag_nopeek ) ) + || abs( last_root_value ) > score_max_eval + || ! record_game.moves + || sec_limit_up == UINT_MAX ) { return 1; } + + ponder_nmove = gen_legal_moves( ptree, ponder_move_list ); + + if ( get_elapsed( &time_start ) < 0 ) { return -1; } + + Out( "\nSearch a move to ponder\n\n" ); + OutCsaShogi( "info ponder start\n" ); + + game_status |= flag_puzzling; + iret = iterate( ptree, 0 ); + game_status &= ~flag_puzzling; + if ( iret < 0 ) { return iret; } + + if ( game_status & ( flag_quit | flag_quit_ponder | flag_suspend ) ) + { + OutCsaShogi( "info ponder end\n" ); + return 1; + } + + if ( abs(last_root_value) > score_max_eval ) + { + OutCsaShogi( "info ponder end\n" ); + return 1; + } + + ponder_move = move = last_pv.a[1]; + str = str_CSA_move( move ); + Out( "\nPonder on %c%s (%+.2f)\n\n", + ach_turn[root_turn], str, (double)last_root_value / 100.0 ); + + iret = make_move_root( ptree, move, ( flag_rep | flag_rejections ) ); + if ( iret < 0 ) + { + OutCsaShogi( "info ponder end\n" ); + return iret; + } + + if ( game_status & mask_game_end ) + { + OutCsaShogi( "info ponder end\n" ); + unmake_move_root( ptree, move ); + return 1; + } + + if ( get_elapsed( &time_start ) < 0 ) { return -1; } + + game_status |= flag_pondering; + + iret = iterate( ptree, 0 ); + if ( game_status & flag_thinking ) + { + game_status &= ~flag_thinking; + if ( iret < 0 ) { return iret; } + + iret = com_turn_start( ptree, flag_from_ponder ); + if ( iret < 0 ) { return iret; } + + return 2; + } + OutCsaShogi( "info ponder end\n" ); + game_status &= ~flag_pondering; + unmake_move_root( ptree, move ); + + return iret; +} + + +#if defined(MNJ_LAN) +int +analyze( tree_t * restrict ptree ) +{ + int iret; + + if ( game_status & mask_game_end ) { return 1; } + + iret = get_elapsed( &time_start ); + if ( iret < 0 ) { return iret; } + + game_status |= flag_pondering; + iret = iterate( ptree, 0 ); + game_status &= ~flag_pondering; + + if ( abs(last_root_value) > score_max_eval ) + { + MnjOut( "pid=%d confident\n", mnj_posi_id ); + } + + return iret; +} +#endif diff --git a/problem.c b/problem.c new file mode 100644 index 0000000..4980e9a --- /dev/null +++ b/problem.c @@ -0,0 +1,102 @@ +#include "shogi.h" + +int +solve_problems( tree_t * restrict ptree, unsigned int nposition ) +{ + const char *str_move; + uint64_t total_node; + unsigned int game_status_save, move, uposition, te1, te0; + int iret, success, failure, ianswer, istatus, iresult; + + success = failure = 0; + total_node = 0; + if ( get_elapsed( &te0 ) < 0 ) { return -1; }; + + for ( uposition = 0; uposition < nposition; uposition++ ) + { + istatus = in_CSA( ptree, &record_problems, NULL, + ( flag_nomake_move | flag_detect_hang + | flag_rejections ) ); + if ( istatus < 0 ) { return istatus; } + + if ( istatus > record_next ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + record_problems.lines, str_bad_record ); + str_error = str_message; + return -2; + } + + /* examine all of answers */ + Out( "Answers:" ); + for ( ianswer = 0; ianswer < MAX_ANSWER; ianswer++ ) + { + str_move = &( record_problems.info.str_move[ianswer][0] ); + if ( str_move[0] == '\0' ) { break; } + if ( ( root_turn && str_move[0] != '-' ) + || ( ! root_turn && str_move[0] != '+' ) ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + record_problems.lines, + "Answers has invalid sign of turn." ); + str_error = str_message; + return -2; + } + + iret = interpret_CSA_move( ptree, &move, str_move+1 ); + if ( iret < 0 ) { return iret; } + + iret = make_move_root( ptree, move, ( flag_detect_hang | flag_rep + | flag_nomake_move ) ); + if ( iret < 0 ) { return iret; } + + Out( "%s ", str_move ); + } + Out( "\n" ); + if ( ! ianswer ) + { + snprintf( str_message, SIZE_MESSAGE, str_fmt_line, + record_problems.lines, + "No answers in the record" ); + str_error = str_message; + return -2; + } + + iret = out_board( ptree, stdout, 0, 0 ); + if ( iret < 0 ) { return iret; } + + if ( get_elapsed( &time_start ) < 0 ) { return -1; }; + time_turn_start = time_start; + + game_status_save = game_status; + game_status |= flag_problem | flag_nopeek | flag_thinking; + iresult = iterate( ptree, 0 ); + game_status = game_status_save; + if ( iresult < 0 ) { return iresult; } + + if ( iresult ) { success++; } + else { failure++; } + + total_node += ptree->node_searched; + + str_move = str_CSA_move( last_pv.a[1] ); + Out( "problem #%d answer=%s -- %s (correct=%d, incorrect=%d)\n\n", + success+failure, str_move, iresult ? "correct" : "incorrect", + success, failure ); + + if ( istatus == record_eof ) { break; } + if ( istatus == record_misc ) + { + iret = record_wind( &record_problems ); + if ( iret < 0 ) { return iret; } + if ( iret == record_eof ) { break; } + } + } + + if ( get_elapsed( &te1 ) < 0 ) { return -1; } + + Out( "Total Nodes: %" PRIu64 "\n", total_node ); + Out( "Total Elapsed: %.2f\n", (double)( te1 - te0 ) / 1000.0 ); + + return 1; +} diff --git a/proce.c b/proce.c new file mode 100644 index 0000000..bb41877 --- /dev/null +++ b/proce.c @@ -0,0 +1,1654 @@ +#include +#include +#include +#include +#include +#include +#include "shogi.h" + +/* unacceptable when the program is thinking, or quit pondering */ +#define AbortDifficultCommand \ + if ( game_status & flag_thinking ) \ + { \ + str_error = str_busy_think; \ + return -2; \ + } \ + else if ( game_status & ( flag_pondering | flag_puzzling ) ) \ + { \ + game_status |= flag_quit_ponder; \ + return 2; \ + } + +#if defined(MINIMUM) +# define CmdBook(x,y) cmd_book(y); +static int cmd_book( char **lasts ); +#else +# define CmdBook(x,y) cmd_book(x,y); +static int cmd_learn( tree_t * restrict ptree, char **lasts ); +static int cmd_book( tree_t * restrict ptree, char **lasts ); +#endif + +#if ! defined(NO_STDOUT) +static int cmd_stress( char **lasts ); +#endif + +#if defined(DEKUNOBOU) +static int cmd_dek( char **lasts ); +#endif + +#if defined(CSA_LAN) +static int proce_csalan( tree_t * restrict ptree ); +static int cmd_connect( tree_t * restrict ptree, char **lasts ); +#endif + +#if defined(MNJ_LAN) +static int proce_mnj( tree_t * restrict ptree ); +static int cmd_mnj( tree_t * restrict ptree, char **lasts ); +static int cmd_mnjmove( tree_t * restrict ptree, char **lasts, int is_alter ); +#endif + +#if defined(TLP) +static int cmd_thread( char **lasts ); +#endif + +#if defined(MPV) +static int cmd_mpv( char **lasts ); +#endif + +static int proce_cui( tree_t * restrict ptree ); +static int cmd_usrmove( tree_t * restrict ptree, const char *str_move, + char **last ); +static int cmd_move_now( void ); +static int cmd_ponder( char **lasts ); +static int cmd_limit( char **lasts ); +static int cmd_quit( void ); +static int cmd_beep( char **lasts ); +static int cmd_peek( char **lasts ); +static int cmd_hash( char **lasts ); +static int cmd_ping( void ); +static int cmd_suspend( void ); +static int cmd_problem( tree_t * restrict ptree, char **lasts ); +static int cmd_display( tree_t * restrict ptree, char **lasts ); +static int cmd_move( tree_t * restrict ptree, char **lasts ); +static int cmd_new( tree_t * restrict ptree, char **lasts ); +static int cmd_read( tree_t * restrict ptree, char **lasts ); +static int cmd_resign( tree_t * restrict ptree, char **lasts ); +static int cmd_time( char **lasts ); +static int is_move( const char *str ); + + +int +procedure( tree_t * restrict ptree ) +{ +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) { return proce_csalan( ptree ); } +#endif +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) { return proce_mnj( ptree ); } +#endif + + return proce_cui( ptree ); +} + + +static int +proce_cui( tree_t * restrict ptree ) +{ + const char *token; + char *last; + + token = strtok_r( str_cmdline, str_delimiters, &last ); + + if ( token == NULL || *token == '#' ) { return 1; } + if ( is_move( token ) ) { return cmd_usrmove( ptree, token, &last ); } + if ( ! strcmp( token, "s" ) ) { return cmd_move_now(); } + if ( ! strcmp( token, "beep" ) ) { return cmd_beep( &last); } + if ( ! strcmp( token, "book" ) ) { return CmdBook( ptree, &last ); } + if ( ! strcmp( token, "display" ) ) { return cmd_display( ptree, &last ); } + if ( ! strcmp( token, "hash" ) ) { return cmd_hash( &last ); } + if ( ! strcmp( token, "limit" ) ) { return cmd_limit( &last ); } + if ( ! strcmp( token, "move" ) ) { return cmd_move( ptree, &last ); } + if ( ! strcmp( token, "new" ) ) { return cmd_new( ptree, &last ); } + if ( ! strcmp( token, "peek" ) ) { return cmd_peek( &last ); } + if ( ! strcmp( token, "ping" ) ) { return cmd_ping(); } + if ( ! strcmp( token, "ponder" ) ) { return cmd_ponder( &last ); } + if ( ! strcmp( token, "problem" ) ) { return cmd_problem( ptree, &last ); } + if ( ! strcmp( token, "quit" ) ) { return cmd_quit(); } + if ( ! strcmp( token, "read" ) ) { return cmd_read( ptree, &last ); } + if ( ! strcmp( token, "resign" ) ) { return cmd_resign( ptree, &last ); } + if ( ! strcmp( token, "suspend" ) ) { return cmd_suspend(); } + if ( ! strcmp( token, "time" ) ) { return cmd_time( &last ); } +#if defined(CSA_LAN) + if ( ! strcmp( token, "connect" ) ) { return cmd_connect( ptree, &last ); } +#endif +#if defined(MNJ_LAN) + if ( ! strcmp( token, "mnj" ) ) { return cmd_mnj( ptree, &last ); } +#endif +#if defined(DEKUNOBOU) + if ( ! strcmp( token, "dekunobou" ) ) { return cmd_dek( &last ); } +#endif +#if defined(MPV) + if ( ! strcmp( token, "mpv" ) ) { return cmd_mpv( &last ); } +#endif +#if defined(TLP) + if ( ! strcmp( token, "tlp" ) ) { return cmd_thread( &last ); } +#endif +#if ! defined(NO_STDOUT) + if ( ! strcmp( token, "stress" ) ) { return cmd_stress( &last ); } +#endif +#if ! defined(MINIMUM) + if ( ! strcmp( token, "learn" ) ) { return cmd_learn( ptree, &last ); } +#endif + + str_error = str_bad_cmdline; + return -2; +} + + +#if defined(CSA_LAN) +static int +proce_csalan( tree_t * restrict ptree ) +{ + const char *token; + char *last; + + token = strtok_r( str_cmdline, str_delimiters, &last ); + + if ( token == NULL ) { return 1; } + if ( *token == ach_turn[client_turn] && is_move( token+1 ) ) + { + char *ptr; + long l; + + token = strtok_r( NULL, str_delimiters, &last ); + if ( token == NULL || *token != 'T' ) + { + str_error = str_bad_cmdline; + return -1; + } + + l = strtol( token+1, &ptr, 0 ); + if ( token+1 == ptr || l == LONG_MAX || l < 1 ) + { + str_error = str_bad_cmdline; + return -1; + } + + adjust_time( (unsigned int)l, client_turn ); + Out( " elapsed: b%u, w%u\n", sec_b_total, sec_w_total ); + return 1; + } + if ( *token == ach_turn[Flip(client_turn)] && is_move( token+1 ) ) + { + return cmd_usrmove( ptree, token+1, &last ); + } + if ( ! strcmp( token, str_resign ) ) { return cmd_resign( ptree, &last ); } + if ( ! strcmp( token, "#WIN" ) + || ! strcmp( token, "#LOSE" ) + || ! strcmp( token, "#DRAW" ) + || ! strcmp( token, "#CHUDAN" ) ) + { + if ( game_status & ( flag_thinking | flag_pondering | flag_puzzling ) ) + { + game_status |= flag_suspend; + return 2; + } + + ShutdownClient; + + if ( client_ngame == client_max_game ) { return cmd_quit(); } + + return client_next_game( ptree, client_str_addr, (int)client_port ); + } + + return 1; +} +#endif + + +#if defined(MNJ_LAN) +static int +proce_mnj( tree_t * restrict ptree ) +{ + const char *token; + char *last; + int iret; + + token = strtok_r( str_cmdline, str_delimiters, &last ); + if ( token == NULL ) { return 1; } + + if ( ! strcmp( token, "new" ) ) + { + iret = cmd_suspend(); + if ( iret != 1 ) { return iret; } + + mnj_posi_id = 0; + iret = cmd_new( ptree, &last ); + if ( iret < 0 ) { return iret; } + + return analyze( ptree ); + } + if ( ! strcmp( token, "idle" ) ) { return cmd_suspend(); } + if ( ! strcmp( token, "alter" ) ) { return cmd_mnjmove( ptree, &last, 1 ); } + if ( ! strcmp( token, "move" ) ) { return cmd_mnjmove( ptree, &last, 0 ); } + + str_error = str_bad_cmdline; + return -2; +} + + +static int +cmd_mnjmove( tree_t * restrict ptree, char **lasts, int is_alter ) +{ + const char *str1 = strtok_r( NULL, str_delimiters, lasts ); + const char *str2 = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long lid; + unsigned int move; + int iret; + + if ( sckt_mnj == SCKT_NULL || str1 == NULL || str2 == NULL ) + { + str_error = str_bad_cmdline; + return -1; + } + + lid = strtol( str2, &ptr, 0 ); + if ( ptr == str2 || lid == LONG_MAX || lid < 1 ) + { + str_error = str_bad_cmdline; + return -1; + } + + AbortDifficultCommand; + + if ( is_alter ) { unmake_move_root( ptree, mnj_move_last ); }; + + iret = interpret_CSA_move( ptree, &move, str1 ); + if ( iret < 0 ) { return iret; } + + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + + mnj_posi_id = (int)lid; + mnj_move_last = move; + + iret = make_move_root( ptree, move, ( flag_history | flag_time | flag_rep + | flag_detect_hang + | flag_rejections ) ); + if ( iret < 0 ) { return iret; } + +# if ! defined(NO_STDOUT) + iret = out_board( ptree, stdout, 0, 0 ); + if ( iret < 0 ) { return iret; } +# endif + + return analyze( ptree ); +} +#endif + + +static int +is_move( const char *str ) +{ + if ( isdigit( (int)str[0] ) && isdigit( (int)str[1] ) + && isdigit( (int)str[2] ) && isdigit( (int)str[3] ) + && isupper( (int)str[4] ) && isupper( (int)str[5] ) + && str[6] == '\0' ) { return 1; } + + return 0; +} + + +static int +cmd_move_now( void ) +{ + if ( game_status & flag_thinking ) { game_status |= flag_move_now; } + + return 1; +} + + +static int +cmd_usrmove( tree_t * restrict ptree, const char *str_move, char **lasts ) +{ + const char *str; + char *ptr; + long lelapsed; + unsigned int move; + int iret; + + if ( game_status & mask_game_end ) + { + str_error = str_game_ended; + return -2; + } + + if ( game_status & flag_thinking ) + { + str_error = str_busy_think; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) { lelapsed = 0; } + else { + if ( *str != 'T' ) + { + str_error = str_bad_cmdline; + return -2; + } + str += 1; + lelapsed = strtol( str, &ptr, 0 ); + if ( ptr == str || lelapsed == LONG_MAX || lelapsed < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + } + + if ( game_status & ( flag_pondering | flag_puzzling ) ) + { + int i; + + for ( i = 0; i < ponder_nmove; i++ ) + { + if ( ! strcmp( str_move, str_CSA_move(ponder_move_list[i]) ) ) + { + break; + } + } + if ( i == ponder_nmove ) + { +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) { AbortDifficultCommand; } +#endif + +#if defined(DEKUNOBOU) + if ( dek_ngame ) { AbortDifficultCommand; } +#endif + +#if defined(CSASHOGI) + AbortDifficultCommand; +#else + str_error = str_illegal_move; + return -2; +#endif + } + + if ( ( game_status & flag_puzzling ) + || strcmp( str_move, str_CSA_move(ponder_move) ) ) + { + ponder_move = MOVE_PONDER_FAILED; + game_status |= flag_quit_ponder; + return 2; + } + else { + iret = renovate_time( Flip(root_turn) ); + if ( iret < 0 ) { return iret; } + if ( lelapsed ) + { + adjust_time( (unsigned int)lelapsed, Flip(root_turn) ); + } + + history_book_learn[ record_game.moves ].move_played = ponder_move; + history_book_learn[ record_game.moves ].hand_played + = ptree->rep_hand_list[ root_nrep-1 ]; + history_book_learn[ record_game.moves ].key_played + = (unsigned int)ptree->rep_board_list[ root_nrep-1 ]; + + out_CSA( ptree, &record_game, ponder_move ); + + game_status &= ~flag_pondering; + game_status |= flag_thinking; + n_nobook_move += 1; + set_search_limit_time( root_turn ); + + OutCsaShogi( "info ponder end\n" ); + + str = str_time_symple( time_turn_start - time_start ); + Out( " %6s MOVE PREDICTION HIT\n" + " elapsed: b%u, w%u\n", str, sec_b_total, sec_w_total ); + return 1; + } + } + + iret = interpret_CSA_move( ptree, &move, str_move ); + if ( iret < 0 ) { return iret; } + move_evasion_pchk = 0; + iret = make_move_root( ptree, move, ( flag_rep | flag_history | flag_time + | flag_rejections + | flag_detect_hang ) ); + if ( iret < 0 ) + { + +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) + { + if ( move_evasion_pchk ) + { + str = str_CSA_move( move_evasion_pchk ); + iret = sckt_out( sckt_csa, "%c%s\n", + ach_turn[Flip(root_turn)], str ); + if ( iret < 0 ) { return iret; } + } + return cmd_suspend(); + } +#endif + +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + if ( move_evasion_pchk ) + { + dek_win += 1; + OutDek( "%%TORYO\n" ); + } + return cmd_suspend(); + } +#endif + + if ( move_evasion_pchk ) + { + str = str_CSA_move( move_evasion_pchk ); +#if defined(CSASHOGI) + OutCsaShogi( "move%s\n", str ); + return cmd_suspend(); +#else + snprintf( str_message, SIZE_MESSAGE, "perpetual check (%c%s)", + ach_turn[Flip(root_turn)], str ); + str_error = str_message; + return -2; +#endif + } + + return iret; + } + + if ( lelapsed ) { adjust_time( (unsigned int)lelapsed, Flip(root_turn) ); } + Out( " elapsed: b%u, w%u\n", sec_b_total, sec_w_total ); + +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL && ( game_status & flag_mated ) ) + { + iret = sckt_out( sckt_csa, "%%TORYO\n" ); + if ( iret < 0 ) { return iret; } + } +#endif + +#if defined(DEKUNOBOU) + if ( dek_ngame && ( game_status & flag_drawn ) ) { OutDek( "%%TORYO\n" ); } +#endif + + if ( ! ( game_status & mask_game_end ) ) + { + iret = com_turn_start( ptree, 0 ); + if ( iret < 0 ) { return iret; } + } + + return 1; +} + + +static int +cmd_beep( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, str_on ) ) { game_status &= ~flag_nobeep; } + else if ( ! strcmp( str, str_off ) ) { game_status |= flag_nobeep; } + else { + str_error = str_bad_cmdline; + return -2; + } + + return 1; +} + + +static int +cmd_peek( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, str_on ) ) { game_status &= ~flag_nopeek; } + else if ( ! strcmp( str, str_off ) ) { game_status |= flag_nopeek; } + else { + str_error = str_bad_cmdline; + return -2; + } + + return 1; +} + + +static int +cmd_ponder( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, str_on ) ) { game_status &= ~flag_noponder; } + else if ( ! strcmp( str, str_off ) ) + { + if ( game_status & ( flag_pondering | flag_puzzling ) ) + { + game_status |= flag_quit_ponder; + } + game_status |= flag_noponder; + } + else { + str_error = str_bad_cmdline; + return -2; + } + + return 1; +} + + +#if ! defined(NO_STDOUT) +static int +cmd_stress( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, str_on ) ) { game_status &= ~flag_nostress; } + else if ( ! strcmp( str, str_off ) ) { game_status |= flag_nostress; } + else { + str_error = str_bad_cmdline; + return -2; + } + + return 1; +} +#endif + + +static int +#if defined(MINIMUM) +cmd_book( char **lasts ) +#else +cmd_book( tree_t * restrict ptree, char **lasts ) +#endif +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + int iret = 1; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + if ( ! strcmp( str, str_on ) ) { iret = book_on(); } + else if ( ! strcmp( str, str_off ) ) { iret = book_off(); } + else if ( ! strcmp( str, "narrow" ) ) { game_status |= flag_narrow_book; } + else if ( ! strcmp( str, "wide" ) ) { game_status &= ~flag_narrow_book; } +#if ! defined(MINIMUM) + else if ( ! strcmp( str, "create" ) ) + { + AbortDifficultCommand; + + iret = book_create( ptree ); + if ( iret < 0 ) { return iret; } + + iret = ini_game( ptree, &min_posi_no_handicap, flag_history, + NULL, NULL ); + if ( iret < 0 ) { return iret; } + + iret = get_elapsed( &time_turn_start ); + } +#endif + else { + str_error = str_bad_cmdline; + iret = -2; + } + + return iret; +} + + +static int +cmd_display( tree_t * restrict ptree, char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + int iret; + + if ( str != NULL ) + { + l = strtol( str, &ptr, 0 ); + if ( ptr == str ) + { + str_error = str_bad_cmdline; + return -2; + } + if ( l == 1 ) { game_status &= ~flag_reverse; } + else if ( l == 2 ) { game_status |= flag_reverse; } + else { + str_error = str_bad_cmdline; + return -2; + } + } + + Out( "\n" ); + iret = out_board( ptree, stdout, 0, 0 ); + if ( iret < 0 ) { return iret; } +#if ! defined(NO_LOGGING) + iret = out_board( ptree, pf_log, 0, 0 ); + if ( iret < 0 ) { return iret; } +#endif + Out( "\n" ); + + return 1; +} + + +static int +cmd_ping( void ) +{ + OutCsaShogi( "pong\n" ); + Out( "pong\n" ); + return 1; +} + + +static int +cmd_hash( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, "learn" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str != NULL && ! strcmp( str, str_on ) ) + { + return hash_learn_on(); + } + else if ( str != NULL && ! strcmp( str, str_off ) ) + { + return hash_learn_off(); + } +#if ! defined(MINIMUM) + else if ( str != NULL && ! strcmp( str, "create" ) ) + { + return hash_learn_create(); + } +#endif + else { + str_error = str_bad_cmdline; + return -2; + } + } + + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 1 || l > 31 ) + { + str_error = str_bad_cmdline; + return -2; + } + + AbortDifficultCommand; + + log2_ntrans_table = (int)l; + memory_free( (void *)ptrans_table_orig ); + return ini_trans_table(); +} + + +static int +cmd_limit( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l1, l2, l3; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + AbortDifficultCommand; + + if ( ! strcmp( str, "depth" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l1 = strtol( str, &ptr, 0 ); + if ( ptr == str || l1 == LONG_MAX || l1 < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + sec_limit_up = UINT_MAX; + node_limit = UINT64_MAX; + depth_limit = (int)l1; + } + else if ( ! strcmp( str, "nodes" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l1 = strtol( str, &ptr, 0 ); + if ( ptr == str || l1 == LONG_MAX || l1 < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + sec_limit_up = UINT_MAX; + depth_limit = PLY_MAX; + node_limit = (uint64_t)l1; + } + else if ( ! strcmp( str, "time" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( ! strcmp( str, "extendable" ) ) + { + game_status |= flag_time_extendable; + } + else if ( ! strcmp( str, "strict" ) ) + { + game_status &= ~flag_time_extendable; + } + else { + l1 = strtol( str, &ptr, 0 ); + if ( ptr == str || l1 == LONG_MAX || l1 < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l2 = strtol( str, &ptr, 0 ); + if ( ptr == str || l2 == LONG_MAX || l2 < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str ) { l3 = -1; } + else { + l3 = strtol( str, &ptr, 0 ); + if ( ptr == str || l3 >= PLY_MAX || l3 < -1 ) + { + str_error = str_bad_cmdline; + return -2; + } + } + + if ( ! ( l1 | l2 ) ) { l2 = 1; } + + depth_limit = PLY_MAX; + node_limit = UINT64_MAX; + sec_limit = (unsigned int)l1 * 60U; + sec_limit_up = (unsigned int)l2; + if ( l3 == -1 ) { sec_limit_depth = UINT_MAX; } + else { sec_limit_depth = (unsigned int)l3; } + } + } + else { + str_error = str_bad_cmdline; + return -2; + } + + return 1; +} + + +static int +cmd_read( tree_t * restrict ptree, char **lasts ) +{ + const char *str1 = strtok_r( NULL, str_delimiters, lasts ); + const char *str2 = strtok_r( NULL, str_delimiters, lasts ); + const char *str3 = strtok_r( NULL, str_delimiters, lasts ); + const char *str_tmp; + FILE *pf_src, *pf_dest; + char str_file[SIZE_FILENAME]; + char *ptr; + unsigned int moves; + long l; + int iret, flag, c; + + flag = flag_history | flag_rep | flag_detect_hang | flag_rejections; + moves = UINT_MAX; + str_tmp = NULL; + + if ( str1 == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( str2 != NULL ) + { + if ( ! strcmp( str2, "t" ) ) { flag |= flag_time; } + else if ( strcmp( str2, "nil" ) ) + { + str_error = str_bad_cmdline; + return -2; + } + } + + if ( str3 != NULL ) + { + l = strtol( str3, &ptr, 0 ); + if ( ptr == str3 || l == LONG_MAX || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + moves = (unsigned int)l - 1U; + } + + AbortDifficultCommand; + + if ( ! strcmp( str1, "." ) ) + { + str_tmp = "game.cs_"; + +#if defined(NO_LOGGING) + strncpy( str_file, "game.csa", SIZE_FILENAME-1 ); +#else + snprintf( str_file, SIZE_FILENAME, "%s/game%03d.csa", + str_dir_logs, irecord_game ); +#endif + pf_dest = file_open( str_tmp, "w" ); + if ( pf_dest == NULL ) { return -2; } + + pf_src = file_open( str_file, "r" ); + if ( pf_src == NULL ) + { + file_close( pf_dest ); + return -2; + } + + while ( ( c = getc(pf_src) ) != EOF ) { putc( c, pf_dest ); } + + iret = file_close( pf_src ); + if ( iret < 0 ) + { + file_close( pf_dest ); + return iret; + } + + iret = file_close( pf_dest ); + if ( iret < 0 ) { return iret; } + + flag |= flag_time; + str1 = str_tmp; + } + + iret = read_record( ptree, str1, moves, flag ); + if ( iret < 0 ) { return iret; } + + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + + if ( str_tmp && remove( str_tmp ) ) + { + out_warning( "remove() failed." ); + return -2; + } + + return 1; +} + + +static int +cmd_resign( tree_t * restrict ptree, char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + + if ( str == NULL || *str == 'T' ) + { + AbortDifficultCommand; + + if ( game_status & mask_game_end ) { return 1; } + +#if defined(DEKUNOBOU) + if ( dek_ngame && record_game.moves < 2 ) + { + str_error = "ignore resignation"; + return -2; + } +#endif + + game_status |= flag_resigned; + renovate_time( root_turn ); + out_CSA( ptree, &record_game, MOVE_RESIGN ); + } + else { + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < MT_CAP_PAWN ) + { + str_error = str_bad_cmdline; + return -2; + } + resign_threshold = (int)l; + } + + return 1; +} + + +static int +cmd_move( tree_t * restrict ptree, char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + unsigned int move; + int iret; + + if ( game_status & mask_game_end ) + { + str_error = str_game_ended; + return -2; + } + + AbortDifficultCommand; + + if ( str == NULL ) + { + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + + iret = com_turn_start( ptree, 0 ); + if ( iret < 0 ) { return iret; } + } + else if ( ! strcmp( str, "restraint" ) ) + { + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + + iret = com_turn_start( ptree, flag_refer_rest ); + if ( iret < 0 ) { return iret; } + } + else { + + iret = interpret_CSA_move( ptree, &move, str ); + if ( iret < 0 ) { return iret; } + + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL ) + { + const char *str2 = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + if ( str2 ) { l = strtol( str2, &ptr, 0 ); } + if ( ! str2 || ptr == str || l == LONG_MAX || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + mnj_posi_id = (int)l; + mnj_move_last = move; + } +#endif + + iret = make_move_root( ptree, move, ( flag_history | flag_time | flag_rep + | flag_detect_hang + | flag_rejections ) ); + if ( iret < 0 ) { return iret; } + } + + return 1; +} + + +static int +cmd_new( tree_t * restrict ptree, char **lasts ) +{ + const char *str1 = strtok_r( NULL, str_delimiters, lasts ); + const char *str2 = strtok_r( NULL, str_delimiters, lasts ); + const min_posi_t *pmp; + min_posi_t min_posi; + int iret; + + AbortDifficultCommand; + + if ( str1 != NULL ) + { + memset( &min_posi.asquare, empty, nsquare ); + min_posi.hand_black = min_posi.hand_white = 0; + iret = read_board_rep1( str1, &min_posi ); + if ( iret < 0 ) { return iret; } + + if ( str2 != NULL ) + { + if ( ! strcmp( str2, "-" ) ) { min_posi.turn_to_move = white; } + else if ( ! strcmp( str2, "+" ) ) { min_posi.turn_to_move = black; } + else { + str_error = str_bad_cmdline; + return -2; + } + } + else { min_posi.turn_to_move = black; } + + pmp = &min_posi; + } + else { pmp = &min_posi_no_handicap; } + + iret = ini_game( ptree, pmp, flag_history, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + return get_elapsed( &time_turn_start ); +} + + +static int +cmd_problem( tree_t * restrict ptree, char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + unsigned int nposition; + int iret; + + if ( str != NULL ) + { + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + nposition = (unsigned int)l; + } + else { nposition = UINT_MAX; } + + AbortDifficultCommand; + + iret = record_open( &record_problems, "problem.csa", mode_read, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + iret = solve_problems( ptree, nposition ); + if ( iret < 0 ) + { + record_close( &record_problems ); + return iret; + } + + iret = record_close( &record_problems ); + if ( iret < 0 ) { return iret; } + + iret = ini_game( ptree, &min_posi_no_handicap, flag_history, NULL, NULL ); + if ( iret < 0 ) { return iret; } + + return get_elapsed( &time_turn_start ); +} + + +static int +cmd_quit( void ) +{ + game_status |= flag_quit; + return 1; +} + + +static int +cmd_suspend( void ) +{ + if ( game_status & ( flag_pondering | flag_puzzling ) ) + { + game_status |= flag_quit_ponder; + return 2; + } + + game_status |= flag_suspend; + return 1; +} + + +static int +cmd_time( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + else if ( ! strcmp( str, "response" ) ) + { + long l; + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 0 || l > 1000 ) + { + str_error = str_bad_cmdline; + return -2; + } + time_response = (unsigned int)l; + return 1; + } + else if ( ! strcmp( str, "remain" ) ) + { + long l1, l2; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l1 = strtol( str, &ptr, 0 ); + if ( ptr == str || l1 == LONG_MAX || l1 < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l2 = strtol( str, &ptr, 0 ); + if ( ptr == str || l2 == LONG_MAX || l2 < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + + if ( sec_limit_up == UINT_MAX ) + { + str_error = str_bad_cmdline; + return -2; + } + + return reset_time( (unsigned int)l1, (unsigned int)l2 ); + } + + str_error = str_bad_cmdline; + return -2; +} + + +#if !defined(MINIMUM) +/* learn (ini|no-ini) steps games iterations tlp1 tlp2 */ +static int +cmd_learn( tree_t * restrict ptree, char **lasts ) +{ + const char *str1 = strtok_r( NULL, str_delimiters, lasts ); + const char *str2 = strtok_r( NULL, str_delimiters, lasts ); + const char *str3 = strtok_r( NULL, str_delimiters, lasts ); + const char *str4 = strtok_r( NULL, str_delimiters, lasts ); +# if defined(TLP) + const char *str5 = strtok_r( NULL, str_delimiters, lasts ); + const char *str6 = strtok_r( NULL, str_delimiters, lasts ); +# endif + char *ptr; + long l; + unsigned int max_games; + int is_ini, nsteps, max_iterations, nworker1, nworker2, iret; + + if ( str1 == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + if ( ! strcmp( str1, "ini" ) ) { is_ini = 1; } + else if ( ! strcmp( str1, "no-ini" ) ) { is_ini = 0; } + else { + str_error = str_bad_cmdline; + return -2; + } + + max_games = UINT_MAX; + max_iterations = INT_MAX; + nworker1 = nworker2 = nsteps = 1; + + if ( str2 != NULL ) + { + l = strtol( str2, &ptr, 0 ); + if ( ptr == str2 || l == LONG_MAX || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + nsteps = (int)l; + } + + if ( str3 != NULL ) + { + l = strtol( str3, &ptr, 0 ); + if ( ptr == str3 || l == LONG_MAX || l == LONG_MIN ) + { + str_error = str_bad_cmdline; + return -2; + } + if ( l > 0 ) { max_games = (unsigned int)l; } + } + + if ( str4 != NULL ) + { + l = strtol( str4, &ptr, 0 ); + if ( ptr == str4 || l == LONG_MAX || l == LONG_MIN ) + { + str_error = str_bad_cmdline; + return -2; + } + if ( l > 0 ) { max_iterations = (int)l; } + } + +# if defined(TLP) + if ( str5 != NULL ) + { + l = strtol( str5, &ptr, 0 ); + if ( ptr == str5 || l > TLP_MAX_THREADS || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + nworker1 = (int)l; + } + + if ( str6 != NULL ) + { + l = strtol( str6, &ptr, 0 ); + if ( ptr == str6 || l > TLP_MAX_THREADS || l < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + nworker2 = (int)l; + } +# endif + + AbortDifficultCommand; + + log2_ntrans_table = 12; + + memory_free( (void *)ptrans_table_orig ); + + iret = ini_trans_table(); + if ( iret < 0 ) { return iret; } + + iret = learn( ptree, is_ini, nsteps, max_games, max_iterations, + nworker1, nworker2 ); + if ( iret < 0 ) { return -1; } + + iret = ini_game( ptree, &min_posi_no_handicap, flag_history, NULL, NULL ); + if ( iret < 0 ) { return -1; } + + iret = get_elapsed( &time_turn_start ); + if ( iret < 0 ) { return iret; } + + return 1; +} +#endif /* MINIMUM */ + + +#if defined(MPV) +static int +cmd_mpv( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + else if ( ! strcmp( str, "num" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 1 || l > MPV_MAX_PV ) + { + str_error = str_bad_cmdline; + return -2; + } + + AbortDifficultCommand; + + mpv_num = (int)l; + + return 1; + } + else if ( ! strcmp( str, "width" ) ) + { + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < MT_CAP_PAWN ) + { + str_error = str_bad_cmdline; + return -2; + } + + AbortDifficultCommand; + + mpv_width = (int)l; + + return 1; + } + + str_error = str_bad_cmdline; + return -2; +} +#endif + + +#if defined(TLP) +static int +cmd_thread( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + else if ( ! strcmp( str, "num" ) ) + { + char *ptr; + long l; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 1 || l > TLP_MAX_THREADS ) + { + str_error = str_bad_cmdline; + return -2; + } + + TlpEnd(); + + tlp_max = (int)l; + + if ( game_status & ( flag_thinking | flag_pondering | flag_puzzling ) ) + { + return tlp_start(); + } + return 1; + } + + str_error = str_bad_cmdline; + return -2; +} +#endif + + +#if defined(CSA_LAN) +static int +cmd_connect( tree_t * restrict ptree, char **lasts ) +{ + const char *str; + char *ptr; + long max_games; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "gserver.computer-shogi.org"; } + strncpy( client_str_addr, str, 255 ); + client_str_addr[255] = '\0'; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "4081"; } + client_port = strtol( str, &ptr, 0 ); + if ( ptr == str || client_port == LONG_MAX || client_port < 0 + || client_port > USHRT_MAX ) + { + str_error = str_bad_cmdline; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "bonanza_test"; } + strncpy( client_str_id, str, 255 ); + client_str_id[255] = '\0'; + + str = strtok_r( NULL, " \t", lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "bonanza_test"; } + strncpy( client_str_pwd, str, 255 ); + client_str_pwd[255] = '\0'; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { client_max_game = INT_MAX; } + else { + max_games = strtol( str, &ptr, 0 ); + if ( ptr == str || max_games == LONG_MAX || max_games < 1 ) + { + str_error = str_bad_cmdline; + return -2; + } + client_max_game = max_games; + } + + AbortDifficultCommand; + + client_ngame = 0; + + return client_next_game( ptree, client_str_addr, (int)client_port ); +} +#endif + + +#if defined(MNJ_LAN) +static int +cmd_mnj( tree_t * restrict ptree, char **lasts ) +{ + char client_str_addr[256]; + char client_str_id[256]; + const char *str; + char *ptr; + unsigned int seed; + int sd; + long l; + int client_port; + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + sd = (int)l; + + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str ) + { + str_error = str_bad_cmdline; + return -2; + } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 0 ) + { + str_error = str_bad_cmdline; + return -2; + } + seed = (unsigned int)l; + + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "localhost"; } + strncpy( client_str_addr, str, 255 ); + client_str_addr[255] = '\0'; + + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "4082"; } + l = strtol( str, &ptr, 0 ); + if ( ptr == str || l == LONG_MAX || l < 0 || l > USHRT_MAX ) + { + str_error = str_bad_cmdline; + return -2; + } + client_port = (int)l; + + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( ! str || ! strcmp( str, "." ) ) { str = "bonanza1"; } + strncpy( client_str_id, str, 255 ); + client_str_id[255] = '\0'; + + AbortDifficultCommand; + + resign_threshold = 65535; + game_status |= ( flag_noponder | flag_noprompt ); + if ( mnj_reset_tbl( sd, seed ) < 0 ) { return -1; } + + sckt_mnj = sckt_connect( client_str_addr, (int)client_port ); + if ( sckt_mnj == SCKT_NULL ) { return -2; } + + str_buffer_cmdline[0] = '\0'; + + Out( "Sending my name %s", client_str_id ); + sckt_out( sckt_mnj, "%s\n", client_str_id ); + + return analyze( ptree ); +} +#endif + + +#if defined(DEKUNOBOU) + +static int +cmd_dek( char **lasts ) +{ + const char *str = strtok_r( NULL, str_delimiters, lasts ); + char *ptr; + long l1, l2; + int iret; + + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + strncpy( str_message, str, SIZE_MESSAGE-1 ); + str_message[SIZE_MESSAGE-1] = '\0'; + dek_ul_addr = inet_addr( str ); + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l1 = strtol( str, &ptr, 0 ); + if ( ptr == str || l1 == LONG_MAX || l1 < 0 || l1 > USHRT_MAX ) + { + str_error = str_bad_cmdline; + return -2; + } + + str = strtok_r( NULL, str_delimiters, lasts ); + if ( str == NULL ) + { + str_error = str_bad_cmdline; + return -2; + } + l2 = strtol( str, &ptr, 0 ); + if ( ptr == str || l2 == LONG_MAX || l2 < 0 || l2 > USHRT_MAX ) + { + str_error = str_bad_cmdline; + return -2; + } + + AbortDifficultCommand; + + iret = dek_start( str_message, (int)l1, (int)l2 ); + if ( iret < 0 ) { return iret; } + + Out( "\n- in communication with Dekunobou...\n" ); + + str_buffer_cmdline[0] = '\0'; + dek_ngame = 1; + dek_lost = 0; + dek_win = 0; + dek_turn = 1; + game_status |= flag_resigned; + + return 1; +} + +#endif + diff --git a/quiesrch.c b/quiesrch.c new file mode 100644 index 0000000..6ceb5fb --- /dev/null +++ b/quiesrch.c @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include "shogi.h" + +/* #define DBG_QSEARCH */ +#if defined(DBG_QSEARCH) +# define DOut( ... ) if ( dbg_flag ) { out( __VA_ARGS__ ); } +#else +# define DOut( ... ) +#endif + +static int gen_next_quies( tree_t * restrict ptree, int alpha, int turn, + int ply, int qui_ply ); + +int +search_quies( tree_t * restrict ptree, int alpha, int beta, int turn, int ply, + int qui_ply ) +{ + int value, alpha_old; + +#if defined(DBG_QSEARCH) + int dbg_flag = 0; + + if ( iteration_depth == 2 && ply == 4 + && ! strcmp( str_CSA_move(ptree->current_move[1]), "7776FU" ) + && ! strcmp( str_CSA_move(ptree->current_move[2]), "3334FU" ) + && ! strcmp( str_CSA_move(ptree->current_move[3]), "8822UM" ) ) + { + dbg_flag = 1; + Out( "qsearch start (alpha=%d beta=%d sp=%d %" PRIu64 ")", + alpha, beta, value, ptree->node_searched ); + } +#endif + + +#if defined(TLP) + if ( ! ptree->tlp_id ) +#endif + { + node_last_check += 1; + } + ptree->node_searched += 1; + ptree->nquies_called += 1; + alpha_old = alpha; + + value = evaluate( ptree, ply, turn ); + + + if ( alpha < value ) + { + if ( beta <= value ) + { + DOut( ", cut by stand-pat\n" ); + MOVE_CURR = MOVE_PASS; + return value; + } + alpha = value; + } + + if ( ply >= PLY_MAX-1 ) + { + if ( alpha_old != alpha ) { pv_close( ptree, ply, no_rep ); } + MOVE_CURR = MOVE_NA; + return value; + } + + + if ( ( qui_ply == 1 && ! ( ply == 2 && InCheck( turn ) ) ) + || ( 1 < qui_ply && qui_ply < QUIES_PLY_LIMIT && ! InCheck( turn ) ) ) + { + MOVE_CURR = IsMateIn1Ply( turn ); + if ( MOVE_CURR ) + { + value = score_mate1ply + 1 - ply; + + if ( alpha < value + && value < beta ) { pv_close( ptree, ply, mate_search ); } + + DOut( "mate found\n" ); + + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + } + + + ptree->anext_move[ply].next_phase = next_quies_gencap; + while ( gen_next_quies( ptree, alpha, turn, ply, qui_ply ) ) + { + DOut( "\nexpand %s (%" PRIu64 ")", + str_CSA_move(MOVE_CURR), ptree->node_searched ); + + MakeMove( turn, MOVE_CURR, ply ); + if ( InCheck(turn) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + + value = -search_quies( ptree, -beta, -alpha, Flip(turn), ply+1, + qui_ply+1 ); + + UnMakeMove( turn, MOVE_CURR, ply ); + + if ( alpha < value ) + { + check_futile_score_quies( ptree, MOVE_CURR, ptree->stand_pat[ply], + -ptree->stand_pat[ply+1], turn ); + if ( beta <= value ) + { + DOut( ", beta cut (%" PRIu64 ")\n", ptree->node_searched ); + + assert( ! IsMove(MOVE_CURR) + || is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + + DOut( ", renew alpha=%d (%" PRIu64 ")\n", + value, ptree->node_searched ); + alpha = value; + } + } + + DOut( "\nall searched (%" PRIu64 ")\n", ptree->node_searched ); + + if ( qui_ply < QUIES_PLY_LIMIT && is_mate_in3ply( ptree, turn, ply ) ) + { + value = score_mate1ply + 1 - ply; + + if ( alpha < value + && value < beta ) { pv_close( ptree, ply, mate_search ); } + + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + + if ( alpha_old != alpha ) + { + if ( alpha == ptree->stand_pat[ply] ) { pv_close( ptree, ply, no_rep ); } + else { pv_copy( ptree, ply ); } + } + + return alpha; +} + + +static int +gen_next_quies( tree_t * restrict ptree, int alpha, int turn, int ply, + int qui_ply ) +{ + switch ( ptree->anext_move[ply].next_phase ) + { + case next_quies_gencap: + { + unsigned int * restrict pmove; + int * restrict psortv; + int i, j, n, nqmove, value, min_score, diff; + unsigned int move; + + ptree->move_last[ply] = GenCaptures( turn, ptree->move_last[ply-1] ); + + /* set sort values */ + pmove = ptree->move_last[ply-1]; + psortv = ptree->sort_value; + nqmove = 0; + n = (int)( ptree->move_last[ply] - pmove ); + + for ( i = 0; i < n; i++ ) + { + move = pmove[i]; + + if ( qui_ply >= QUIES_PLY_LIMIT + && ( ( UToCap(move) == pawn && ! I2IsPromote(move) ) + || ( ! UToCap(move) && I2PieceMove(move) != pawn ) ) ) + { + continue; + } + + diff = estimate_score_diff( ptree, move, turn ); + min_score = eval_max_score( ptree, move, ptree->stand_pat[ply], + turn, diff ); + + if ( alpha < min_score ) + { + value = swap( ptree, move, -1, MT_CAP_SILVER, turn ); + if ( -1 < value ) + { + psortv[nqmove] = value + diff; + pmove[nqmove++] = move; + } + } + } + + /* insertion sort */ + psortv[nqmove] = INT_MIN; + for ( i = nqmove-2; i >= 0; i-- ) + { + value = psortv[i]; move = pmove[i]; + for ( j = i+1; psortv[j] > value; j++ ) + { + psortv[j-1] = psortv[j]; pmove[j-1] = pmove[j]; + } + psortv[j-1] = value; pmove[j-1] = move; + } + + ptree->move_last[ply] = ptree->move_last[ply-1] + nqmove; + ptree->anext_move[ply].move_last = pmove; + ptree->anext_move[ply].next_phase = next_quies_captures; + } + + case next_quies_captures: + if ( ptree->anext_move[ply].move_last != ptree->move_last[ply] ) + { + MOVE_CURR = *ptree->anext_move[ply].move_last++; + return 1; + } + } + + return 0; +} diff --git a/rand.c b/rand.c new file mode 100644 index 0000000..2974cd0 --- /dev/null +++ b/rand.c @@ -0,0 +1,90 @@ +#include "shogi.h" + +/* PRNG based on Mersenne Twister (M. Matsumoto and T. Nishimura, 1998). */ +#define RAND_M 397 +#define MASK_U 0x80000000U +#define MASK_L 0x7fffffffU +#define MASK32 0xffffffffU + +void +ini_rand( unsigned int u ) +{ + int i; + + rand_work.count = RAND_N; + rand_work.cnst[0] = 0; + rand_work.cnst[1] = 0x9908b0dfU; + + u &= MASK32; + rand_work.vec[0] = u; + for ( i = 1; i < RAND_N; i++ ) + { + u = i + 1812433253U * ( u ^ ( u >> 30 ) ); + u &= MASK32; + rand_work.vec[i] = u; + } +} + + +unsigned int +rand32( void ) +{ + unsigned int u, u0, u1, u2; + int i; + + if ( rand_work.count == RAND_N ) + { + rand_work.count = 0; + + for ( i = 0; i < RAND_N-RAND_M; i++ ) + { + u = rand_work.vec[i] & MASK_U; + u |= rand_work.vec[i+1] & MASK_L; + + u0 = rand_work.vec[ i + RAND_M ]; + u1 = u >> 1; + u2 = rand_work.cnst[ u & 1 ]; + + rand_work.vec[i] = u0 ^ u1 ^ u2; + } + + for ( ; i < RAND_N-1 ;i++ ) + { + u = rand_work.vec[i] & MASK_U; + u |= rand_work.vec[i+1] & MASK_L; + + u0 = rand_work.vec[ i + RAND_M - RAND_N ]; + u1 = u >> 1; + u2 = rand_work.cnst[ u & 1 ]; + + rand_work.vec[i] = u0 ^ u1 ^ u2; + } + + u = rand_work.vec[RAND_N-1] & MASK_U; + u |= rand_work.vec[0] & MASK_L; + + u0 = rand_work.vec[ RAND_M - 1 ]; + u1 = u >> 1; + u2 = rand_work.cnst[ u & 1 ]; + + rand_work.vec[RAND_N-1] = u0 ^ u1 ^ u2; + } + + u = rand_work.vec[ rand_work.count++ ]; + u ^= ( u >> 11 ); + u ^= ( u << 7 ) & 0x9d2c5680U; + u ^= ( u << 15 ) & 0xefc60000U; + u ^= ( u >> 18 ); + + return u; +} + + +uint64_t +rand64( void ) +{ + uint64_t h = rand32(); + uint64_t l = rand32(); + + return l | ( h << 32 ); +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..7aaf0fb --- /dev/null +++ b/readme.txt @@ -0,0 +1,562 @@ +---------------------------------------------------------------------- + Bonanza Feliz 0.0 - Executable Source Code + Kunihito Hoki, 1 Apr 2010 +---------------------------------------------------------------------- + + +1. Introduction +---------------- + +Bonanza is a state-of-the art computer shogi engine which runs on +Windows and Linux machines, and this directory contains a +platform-independent source code. + +This source code is distributed with a hope that it will be useful in +addition to the main part of the shogi engine. This program includes +many useful functions such as manipulating a shogi board, reading and +writing a CSA record file, speaking of a CSA protocol with a socket +communication, and controlling time, and etc.. I believe this program +can be a good starting point if you are interested in making shogi +programs. + +One main feature of this program is that it employs a brute-force +search method together with bitboard techniques as many chess programs +do. Another notable feature is a machine learning of shogi evaluation +functions. The details of the learning algorithm (aka Bonanza method) +were already presented [1], and this source code provides an example +of implementation of the learning method. + +I admit that some parts of the source code are cryptic, e.g. codes in +"mate1ply.c". I hope that I will have some time to make a quality +documentation and comments on the program code, or someone else could +decrypt my program and provide a documentation. + +Any comments or suggestions are welcome [2]. + +[1] K. Hoki, "Optimal control of minimax search results to learn + positional evaluation", Game Programming Workshop, Hakone, Japan + 2008. + +[2] Contact to "bonanza_query [at] hotmail.com". + + +2. Legal Notices +----------------- + +This program is protected by copyright. Without a specific +permission, any means of commercial applications are prohibited. +Furthermore, this program is distributed without any warranty. +Within these limits, you can use, redistribute, and/or modify it. + + +3. Change Logs +--------------- + +Feliz 0.0 + +- Some results from Y. Sato's experiments with Bonanza inspired me + to use the history table in LMR in Shogi, as Fruit did in chess. By + doing so, some improvement in performance of the tree search is + made. + +- From private communications with Yaneurao, it turned out that the + 3-ply-mate detections in 'mate3.c' can search game trees more + efficiently by using several heuristic pruning techniques. Now + 'mate3.c' adopts some of these techniques. + +- E. Ito kindly reported two bugs in the source codes. One is in + hash_store(), and the other is in ehash_probe() and + ehash_store(). Now these are fixed. + +- The structure of history table is modified from 'square-to' and + 'square-from' method to an exact match method by using the perfect + hash technique. The hash function of moves in 'phash.c' is generated + by codes at http://burtleburtle.net/bob/hash/perfect.html. + +- A command 'mnj' is added to connect to the cluster-computing server + in src/cluster/. + +- In client_next_game(), the way of dealing with REJECT message from a + CSA Shogi server is revised. Now Bonanza connects to the server again + when the game is rejected by an opponent. + + +Version 4.1.3 +- The owner of 'LS3600 Blog Webpage' pointed out that there were two + serious bugs. Thank you very much! According to his indication, + is_hand_eq_supe() in 'utility.c' and read_CSA_line() in 'csa.c' are + revised. +- In MPV code in 'searchr.c', a test of 'root_abort' flag had been + forgotten. Now the flag is tested after every call of search(). +- 'lan.txt' is added to 'src/executable/' directory. This is an + example of an input sequence to connect to CSA Shogi server. +- 'book.bin' now has smaller moves and positions than previous one does. + Also, 'book_anti.csa' is added to 'src/executable/' directory. This + is an example of bad moves which apear in records of human experts. +- 'Legal Notices' in this document is corrected. + +Version 4.1.2 +- In 'Makefile' and 'Makefile.vs', targets which require profile-guided + optimization are removed. Furthermore, an option, which controls + optimization, has been reverted from the aggressive flag, -O3, to a + moderate one, '-O2'. These modifications were necessary for avoiding + abnormal terminations of the program. +- In 'ini.c', the attribute of POSIX thread in a global-variable + 'pthread_attr' is set to 'detach-state'. Because threads will never + join with any other threads in this program, the thread should be + created in the detached state to free system resources. +- In 'ini.c', the default size of the transposition table is lifted + from 12MByte to 48MByte. +- In 'iterate.c", probing the opening book is avoided when the move + history of a current game has repetitions. +- In 'shogi.h', the margins of futility pruning are increased in + accord with new positional evaluation of the feature vector in + 'fv.bin'. +- The quality of an opening book, 'winbin/book.bin', has been improved + at the expense of quantity. + +Version 4.1.0 and 4.1.1 (26 Apr 2009) +- In 'Makefile' and 'Makefile.vs', some options of Intel C compiler + are modified. Here, agressive optimization '-O3' is substituted for + the default '-O2', pthreads support '-pthread' is substituted for + '-lpthread', and an obsolete '-static-libcxx' is removed. +- In 'Makefile', the conformance of GNU C and Intel C compilers are + set to GNU extensions of ISO C99 by setting '-std=gnu99' because a + POSIX function 'strtok_r()' is not in the C99 standard library. +- In 'Makefile', targets for GNU C with gprof and profile-guided + optimization of GNU C are removed. +- In 'shogi.h', inline assemblies and intrinsics are used on x86-64 as + well as x86. To detect the targets, pre-defined macros '__i386__' + and '__x86_64__' are examined. +- In 'evaluate.c', the evaluation function looks up the table + 'stand_pat[ply]' to see if the position have been evaluated since + the previous move is made by 'make_move_[bw]()' functions. Also, the + evaluation function probes the hash table 'ehash_tbl[]' to avoid + re-evaluation of the same position. +- In 'io.c', an immediate value 0 is substituted for 'fileno(stdin)' + because the POSIX function 'fileno()' is not part of ANSI C. POSIX + requires that the file descriptor associated with 'stdin' be 0. +- In 'iterate.c', a criterion for aging of transposition-table, i.e., + increment of a global variable 'trans_table_age', is lifted from 7% + to 9% of saturation ratio of the table. +- In 'learn1.c', the macro 'SEARCH_DEPTH' is set to 2. The macro + specifies the depth threashold of searches for finding all of + principle variations of positions in a set of games. +- In 'learn1.c', a step size of increment/decrement of feature vectors + is fixed to 1 in 'learn_parse2()' function. +- In 'hash.c', when a node can be pruned by using a hash value based on + futility pruning, a return value of the node is set to beta. +- In 'rand.c', some codes of initialization of PRNG variables are + moved to 'ini_genrand()' from 'ini()' function. +- In 'sckt.c', a function 'send_crlf()' is removed. +- In 'search.c', now 'search()' calls 'search_quies()' and returns if + a search depth reaches a threashold. So that we can call 'search()' + function anytime, and this makes source codes simple. +- In 'search.c', the static evaluation value is evaluated and used to + see if the late-move reduction is applicable or not. The evaluation + value is also used by the futility pruning. +- In 'search.c', the futility pruning is not applied if the node is in + check or the move is a check. +- In 'search.c', now Bonanza sends the keep-alive command '0x0a' to + the server in 'detect_signals()'. +- In 'time.c', 'set_seach_limit_time()' is simplified. +- In 'time.c', the second argument of 'gettimeofday()' is set to 'NULL'. + +Version 4.0.4 (2 Feb 2009) +- An error of GCC inline assembly for spinlock in "thread.c" is fixed. +- In Windows OS, Bonanza now opens all streams with file sharing by + using "_SH_DENYNO" constant in "io.c". +- GCC built-in functions are substituted for GCC inline assemblies for + bit-scan operations in "bitop.h". Furthermore, "bitop.h" is removed, + and some of macros in the header are integrated into "shogi.h". + +Version 4.0.3 (Jan 2008) + + +4. Files +--------- + +Here is a list of files you can find in this directory. + +C headers +- param.h piece values +- shogi.h main header + +basic C functions +- main.c main function of C program +- data.c definition of global variables +- ini.c initializations +- rand.c pseudo random number generator +- time.c time functions +- bitop.c bit operation +- utility.c misc. functions + +I/O +- proce.c input procedure +- csa.c csa file format I/O +- io.c basic I/O +- dek.c dekunobou +- sckt.c TCP/IP client of CSA SHOGI protocol + +bitboard manipulations +- attack.c piece attacks +- genchk.c move generation (checks) +- genevasn.c move generation (evasions) +- gendrop.c move generation (drops) +- gennocap.c move generation (non-captures) +- gencap.c move generation (captures) +- movgenex.c move generation (inferior moves) +- makemove.c make moves +- unmake.c unmake move +- mate1ply.c 1-ply mate detection +- debug.c examine bitboard validity + +brute-force search +- iterate.c iterative deepning search at root node +- searchr.c alpha-beta search at root node +- search.c alpha-beta search +- next.c obtains next move +- quiesrch.c quiescence search +- evaluate.c static eveluation function +- evaldiff.c easy and fast evaluation function +- swap.c static exchange evaluation +- hash.c transposition table +- thread.c thread-level parallelization +- root.c root move genelation and shallow min-max search +- mate3.c 3-ply mate detection +- ponder.c pondering +- book.c creates and probes opening book +- problem.c auto problem solver +- valid.c examine move validity + +optimal control of min-max search +- learn1.c main functions +- learn2.c feture vector manipuration + +misc. +- bonanza.txt which now you are looking at +- Makefile makefile for gnu make.exe +- Makefile.vs makefile for Microsoft nmake.exe +- bonanza.ico icon file for windows +- bonanza.rc resource-definition file for windows +- lan.txt example of input sequence to connect CSA Shogi server +- book_anti.csa example of a set of bad moves which apear in records + of human exparts. This is used by 'book create' command. + +4. How to build Bonanza +----------------------- + +You can build Bonanza by means of GNU Make on Linux or Microsoft NMAKE +on Windows. Here is some examples: + +- GCC on Linux +> make -f Makefile gcc + +- Intel C++ Compiler on Linux +> make -f Makefile icc + +- Microsoft C/C++ Compiler on Windows +> nmake -f Makefile.vs cl + +- Intel C++ Compiler on Windows +> nmake -f Makefile.vs icl + +The C source codes are written by using ANSI C plus a small number of +new features in ISO C99. Therefore, I think this can be easily built +in many platforms without much effort. + +It may be necessary to define some macros in Makefile or +Makefile.vs. The macros are: + +- NDEBUG (DEBUG) builds release (debug) version of Bonanza + +- MINIMUM disables some auxiliary functions that are not + necessary to play a game, e.g., book composition + and optimization of evaluation functions + +- TLP enables thread-level parallel search + +- MPV enables multi-PV search + +- CSA_LAN enables Bonanza to communicate by CSA Shogi TCP/IP + protcol + +- DEKUNOBOU enables dekunobou interface (available only for + Windows) + +- CSASHOGI builds an engine for CSA Shogi (available only for + Windows) + +- NO_LOGGING suppresses dumping log files + +Bonanza is an application that does not provide graphical user +interface. If you could build "bonanza.exe" properly without CSASHOGI +macro, it shows a prompt "Black 1>" when you execute it at a computer +console. + +Bonanza uses three binary files: a feature vector of static evaluation +function "fv.bin", an opening book "book.bin", and a +position-learning database "hash.bin". You can find these in "winbin/" +directory. Without the NO_LOGGING option, Bonanza must find "log/" +directory to dump log files. + + +5. Command List +--------------- + +- beep on +- beep off + These commands enable (on) or disable (off) a beep when Bonanza + makes a move. The default is on. + +- book on +- book off + These commands enable (on) or disable (off) to probe the opening + book, "./book.bin". The default is on. + +- book narrow +- book wide + When the command with "narrow" is used, Bonanza selects a book + move from a small set of opening moves. The default is "wide". The + narrowing of the opening moves is useful if you want Bonanza + choose a common opening line. + +- book create + This command creates the opening book file, "./book.bin", by using + numerous experts' games in a single CSA record file, "./book.csa". + It also uses another CSA record file, "book_anti.csa", where you + can register bad moves that may appear in the experts' games at + the last moves in the record file. Here is the example: + + ---------------------------------------- + PI, +, +6978KI, %TORYO + / + PI, +, +6978KI, -8384FU, %TORYO + / + PI, +, +7776FU, -4132KI, %TORYO + / + PI, +, +7776FU, -4132KI, +2726FU, %TORYO + ---------------------------------------- + + This command becomes effective when MINIMUM macro is not defined + in the Makefile. + +- connect 'addr' 'port' 'id' 'passwd' ['ngame'] + This command connects Bonanza to a shogi server by using the CSA + protocol. The first four arguments specify the network address, + port number, user ID, and password, respectively. The last + argument limits a number of games that will be played by Bonanza. + This command becomes effective when CSA_LAN macro is defined in + the Makefile. + +- dekunobou 'addr' 'port-dekunobou' 'port-bonanza' + This command connects Bonanza to Dekunobou. + +- display ['num'] + This command prints the shogi board. If you want to flip the + board, set 'num' to 2. If not, set it to 1. + +- s + Bonanza makes a prompt reply while thinking as soon as this + command is used. + +- hash 'num' + This command is used to initialize the transposition table and + set the size of the table to 2^'num'. + +- hash learn create + This command is used to make a zero-filled position-lerning + database, "hash.bin". This command becomes effective when MINIMUM + macro is not defined in the Makefile. + +- hash learn on +- hash learn off + These commands enable (on) or disable (off) the position learning. + The default is on. + +- learn 'str' 'steps' ['games' ['iterations' ['num1' ['num2']]]] + This command optimizes a feature vector of the static evaluation + function by using numorous experts' games in a single CSA record + file, "./records.csa". If you want to use a zero-filled vector as + an initial guess of the optimization procedure, set 'str' to + "ini". If not, set it to "no-ini". The third argument 'games' is a + number of games to be read from the record file. If the third + argument is negative or omitted, all games are read from the file. + + The learning method iterates a set of procedures, and the number + of iteration can be limited by the fourth argument. It continues + as long as the argument is negative. The procedures consist of two + parts. The first part reads the record file and creates principal + variations by using 'num1' threads. The default value of 'num1' is + 1. The second part renews the feature vector 'steps' times by using + 'num2' threads in accord with the principal variations. The default + value of 'steps' and 'num2' is 1. Note that each thread in the + second procedure uses about 500MByte of the main memory. The two + arguments 'num1' and 'num2' become effective when TLP macro is + defined in the Makefile. After the procedures, the optimized + vector is saved in "./fv.bin". This command become effective when + MINIMUM macro is not defined in the Makefile. + +- limit depth 'num' + This command is used to specify a depth, 'num', at which Bonanza + ends the iterative deepening search. + +- limit nodes 'num' + When this command is used, Bonanza stops thinking after searched + nodes reach to 'num'. + +- limit time 'minute' 'second' ['depth'] + This command limits thinking time of Bonanza. It tries to make + each move by consuming the time 'minute'. When the time is spent + all, it makes each move in 'second'. The last argument 'depth' can + be used if you want Bonanza to stop thinking after the iterative + deepening searches reach sufficient depth. + +- limit time extendable +- limit time strict + The command, "limit time extendable", allows Bonanza to think + longer than the time limited by the previous command if it wishes + to. The default is "strict". + +- mnj 'sd' 'seed' 'addr' 'port' 'id' + This command connects Bonanza to the council server in + src/cluster/. The first two integers specify the standard + deviation and initial seed of pseudo-random numbers which are + added to the static evaluation function. Experiments suggested + that an appropriate value for the standard deviation is 50. Note + that all clients should use different seeds. The last three + arguments are network address, port number, user ID, + respectively. This command becomes effective when MNJ_LAN macro is + defined in the Makefile. + +- move ['str'] + Bonanza makes a move of 'str'. If the argument is omitted, Bonanza + thinks of its next move by itself. + +- mpv num 'nroot' +- mpv width 'threshold' + These commands control the number of root moves, 'nroot', to + constitute principal variations. The default number is 1. A root + move that yields a smaller value than the best value by 'threshold' + is neglected. The default threshold is about 200. These commands + become effective when MPV macro is defined in the Makefile. + +- new ['str'] + This command initializes the shogi board. The argument 'str' + controls an initial configuration of the board. If you want to + play a no-handicapped game, set 'str' to "PI" and this is the + default value. In a handicapped game, specify squares and pieces + to drop, e.g. "new PI82HI22KA" or "new PI19KY". + +- peek on +- peek off + The command "peek on (off)" enables (disables) peeks at a buffer + of the standard input file while Bonanza is thinking. The default + is on. This command is useful when you want to process a set of + commands as "> ./bonanza.exe < infile". + +- ping + Prompt Bonanza to print "pong". + +- ponder on +- ponder off + The command "ponder on (off)" enables (disables) thinks on the + opponent's time. The default is on. + +- problem ['num'] + This command is used to solve problems in "./problem.csa". Here + is an example of the problem file. + + ----------------------------- + $ANSWER:+0024FU + P1-KY-KE-OU-KI * * * * -KY + P2 * * * * * -KI * * * + P3 * * -FU-GI-FU * -KE * -KA + P4-FU * * -FU-GI-FU-HI * -FU + P5 * * * * * * * -FU+KY + P6+FU+KA+FU+FU+GI+FU+KI * * + P7 * +FU * * +FU * * * * + P8 * +OU+KI+GI * * +HI * * + P9+KY+KE * * * * * +KE * + P+00FU00FU + P-00FU00FU00FU + + + / + $ANSWER:+0087KY:+0088KY + P1-OU-KE * * * * * +GI * + P2-KY-KI * * * * * * * + P3-FU-HI * -KI * * -GI * * + P4 * * -KE * * * * * -FU + P5 * +GI * -FU-FU-FU-FU-FU * + P6+FU+HI-FU * * * * * * + P7 * * * +FU * * * * +FU + P8 * * +OU+KI+KI * * * * + P9+KY+KE * * * * * +KE+KY + P+00KA00GI00KY00FU00FU + P-00KA00FU00FU00FU00FU00FU + + + ----------------------------- + + The argument 'num' specifies the number of problems to solve. + +- quit + The quit command and EOF character will exit Bonanza. + +- read 'filename' [(t|nil) ['num']] + This command is used to read a CSA record 'filename' up to 'num' + moves. Set the second argument to "nil" when you want to ignore + time information in the record. The default value is "t". Bonanza + reads all move sequence if the last argument is neglected. If + 'filename' is ".", the command reads an ongoing game from the + initial position. + +- resign + Use this command when you resign a game. + +- resign 'num' + This command specifies the threshold to resign. 'num' is a value + of the threshold. The default is around 1000. + +- stress on +- stress off + When the command "stress on" is used, last-move shown in shogi + board is stressed. The default is on. + +- time remain 'num1' 'num2' + This command tells Bonanza the remaining time. 'num1' ('num2') is + the remaining time of black (white) in seconds. + +- time response 'num' + This command specifies a margin to control time. The time margin + saves Bonanza from time up due to TCP/IP communication to a server + program, sudden disc access, or imperfection of time control of + Bonanza. 'num' is the time margin in milli-second. The default + value is 200. + +- tlp 'num' + This command controls the number of threads to be created when + Bonana considers a move to make. The command becomes effective + when TLP macro is defined in the Makefile. 'num' is the number of + threads. The default value is 1. + +- # + A line beginning with # causes all characters on that line + to be ignored. + +- [move command] + A move command consists of four digits followed by two + capital alphabets, e.g. 7776FU. The first two digits + are a starting square and the last two are a target square. The + starting square is "OO" if the move is a dorp, e.g. 0087FU. The + following two alphabets specify a piece type as the following, + + FU - pawn (Fuhyo) TO - promoted pawn (Tokin) + KY - lance (Kyousha) NY - promoted lance (Narikyo) + KE - knight (Keima) NK - promoted knight (Narikei) + GI - silver general (Ginsho) NG - promoted silver (Narigin) + KI - gold general (Kinsyo) + KA - Bishop (Kakugyo) UM - Dragon horse (Ryuma) + HI - Rook (Hisha) RY - Dragon king (Ryuo) + OU - King (Osho) + + Here, words in parentheses are romanization of Japanese words. diff --git a/root.c b/root.c new file mode 100644 index 0000000..e778431 --- /dev/null +++ b/root.c @@ -0,0 +1,249 @@ +#include +#include +#include "shogi.h" + +static int read_rest_list( tree_t * restrict ptree, + unsigned int * restrict pmove_list ); + +static int is_move_rest( unsigned int move, + const unsigned int * restrict pmove_restraint ); + +int +make_root_move_list( tree_t * restrict ptree, int flag ) +{ + unsigned int * restrict pmove; + unsigned int arestraint_list[ MAX_LEGAL_MOVES ]; + int asort[ MAX_LEGAL_MOVES ]; + unsigned int move, move_best; + int i, j, k, h, value, num_root_move, iret, value_pre_pv; + int value_best; + + if ( flag & flag_refer_rest ) { read_rest_list( ptree, arestraint_list ); } + else { arestraint_list[0] = 0; } + + pmove = ptree->move_last[0]; + ptree->move_last[1] = GenCaptures( root_turn, pmove ); + ptree->move_last[1] = GenNoCaptures( root_turn, ptree->move_last[1] ); + ptree->move_last[1] = GenDrop( root_turn, ptree->move_last[1] ); + num_root_move = (int)( ptree->move_last[1] - pmove ); + + value_pre_pv = INT_MIN; + move_best = 0; + value_best = 0; + for ( i = 0; i < num_root_move; i++ ) + { + if ( ! ( game_status & flag_nopeek ) + && ( game_status & ( flag_puzzling | flag_pondering ) ) + && i != 0 + && ( i % 8 ) == 0 ) + { + iret = next_cmdline( 0 ); + if ( iret == -1 ) + { + game_status |= flag_search_error; + return 1; + } + else if ( iret == -2 ) + { + out_warning( "%s", str_error ); + ShutdownClient; + } + else if ( game_status & flag_quit ) { return 1; } + else if ( iret ) + { + iret = procedure( ptree ); + if ( iret == -1 ) + { + game_status |= flag_search_error; + next_cmdline( 1 ); + return 1; + } + else if ( iret == -2 ) + { + out_warning( "%s", str_error ); + next_cmdline( 1 ); + ShutdownClient; + } + else if ( iret == 1 ) { next_cmdline( 1 ); } + + if ( game_status & ( flag_quit | flag_quit_ponder + | flag_suspend ) ) + { + return 1; + } + } + } + + value = INT_MIN; + move = pmove[i]; + + MakeMove( root_turn, move, 1 ); + if ( ! InCheck( root_turn ) + && ! is_move_rest( move, arestraint_list ) ) + { + iret = no_rep; + if ( InCheck(Flip(root_turn)) ) + { + ptree->nsuc_check[2] + = (unsigned char)( ptree->nsuc_check[0] + 1U ); + if ( ptree->nsuc_check[2] >= 2 * 2 ) + { + iret = detect_repetition( ptree, 2, Flip(root_turn), 2 ); + } + } + + if ( iret == perpetual_check ) { value = INT_MIN; } + else { + ptree->current_move[1] = move; + value = -search_quies( ptree, -score_bound, score_bound, + Flip(root_turn), 2, 1 ); + if ( value > value_best ) + { + value_best = value; + move_best = move; + } + if ( I2IsPromote(move) ) { value++; } + if ( move == ptree->pv[0].a[1] ) + { + value_pre_pv = value; + value = INT_MAX; + } + } + } + UnMakeMove( root_turn, move, 1 ); + asort[i] = value; + } + if ( UToCap(move_best) ) { root_move_cap = 1; } + + /* shell sort */ + for ( k = SHELL_H_LEN - 1; k >= 0; k-- ) + { + h = ashell_h[k]; + for ( i = num_root_move-h-1; i >= 0; i-- ) + { + value = asort[i]; + move = pmove[i]; + for ( j = i+h; j < num_root_move && asort[j] > value; j += h ) + { + asort[j-h] = asort[j]; + pmove[j-h] = pmove[j]; + } + asort[j-h] = value; + pmove[j-h] = move; + } + } + + /* discard all of moves cause mate or perpetual check */ + if ( asort[0] >= -score_max_eval ) + { + for ( ; num_root_move; num_root_move-- ) + { + if ( asort[num_root_move-1] >= -score_max_eval ) { break; } + } + } + + /* discard perpetual checks */ + else for ( ; num_root_move; num_root_move-- ) + { + if ( asort[num_root_move-1] != INT_MIN ) { break; } + } + + for ( i = 0; i < num_root_move; i++ ) + { + root_move_list[i].move = pmove[i]; + root_move_list[i].nodes = 0; + root_move_list[i].status = 0; + } + if ( value_pre_pv != INT_MIN ) { asort[0] = value_pre_pv; } + root_nmove = num_root_move; + + if ( num_root_move > 1 && ! ( game_status & flag_puzzling ) ) + { + int id_easy_move = 0; + + if ( asort[0] > asort[1] + ( MT_CAP_DRAGON * 3 ) / 8 ) + { + id_easy_move = 3; + easy_min = - ( MT_CAP_DRAGON * 4 ) / 16; + easy_max = ( MT_CAP_DRAGON * 32 ) / 16; + easy_abs = ( MT_CAP_DRAGON * 19 ) / 16; + } + else if ( asort[0] > asort[1] + ( MT_CAP_DRAGON * 2 ) / 8 ) + { + id_easy_move = 2; + easy_min = - ( MT_CAP_DRAGON * 3 ) / 16; + easy_max = ( MT_CAP_DRAGON * 6 ) / 16; + easy_abs = ( MT_CAP_DRAGON * 9 ) / 16; + } + else if ( asort[0] > asort[1] + MT_CAP_DRAGON / 8 + && asort[0] > - MT_CAP_DRAGON / 8 + && I2From(pmove[0]) < nsquare ) + { + id_easy_move = 1; + easy_min = - ( MT_CAP_DRAGON * 2 ) / 16; + easy_max = ( MT_CAP_DRAGON * 4 ) / 16; + easy_abs = ( MT_CAP_DRAGON * 6 ) / 16; + } + + if ( easy_abs ) + { + Out( "\n the root move %s looks easy (type %d).\n", + str_CSA_move(pmove[0]), id_easy_move ); + Out( " evasion:%d, capture:%d, promotion:%d, drop:%d, " + "value:%5d - %5d\n", + ptree->nsuc_check[1] ? 1 : 0, + UToCap(pmove[0]) ? 1 : 0, + I2IsPromote(pmove[0]) ? 1 : 0, + I2From(pmove[0]) >= nsquare ? 1 : 0, + asort[0], asort[1] ); + easy_value = asort[0]; + } + } + + return asort[0]; +} + + +static int +read_rest_list( tree_t * restrict ptree, unsigned int * restrict pmove_list ) +{ + FILE *pf; + int iret, imove; + char a[65536]; + + pf = file_open( "restraint.dat", "r" ); + if ( pf == NULL ) { return -2; } + + for ( imove = 0; imove < MAX_LEGAL_MOVES; imove++ ) + { +#if defined(_MSC_VER) + iret = fscanf_s( pf, "%s\n", a, 65536 ); +#else + iret = fscanf( pf, "%s\n", a ); +#endif + if ( iret != 1 ) { break; } + iret = interpret_CSA_move( ptree, pmove_list+imove, a ); + if ( iret < 0 ) + { + file_close( pf ); + return iret; + } + } + + pmove_list[imove] = 0; + + return file_close( pf ); +} + + +static int +is_move_rest( unsigned int move, + const unsigned int * restrict pmove_restraint ) +{ + while ( *pmove_restraint ) + { + if ( move == *pmove_restraint++ ) { return 1; } + } + + return 0; +} diff --git a/sckt.c b/sckt.c new file mode 100644 index 0000000..9bf0f3f --- /dev/null +++ b/sckt.c @@ -0,0 +1,364 @@ +#include +#include +#include +#if ! defined(_WIN32) +# include +# include +# include +# include +# include +#endif +#include "shogi.h" + +#if defined(CSA_LAN) || defined(DEKUNOBOU) || defined(MNJ_LAN) +const char * +str_WSAError( const char *str ) +{ +# if defined(_WIN32) + snprintf( str_message, SIZE_MESSAGE, "%s:%d", str, WSAGetLastError() ); + str_message[SIZE_MESSAGE-1] = '\0'; + return str_message; +# else + return str; +# endif +} +#endif + + +#if defined(CSA_LAN) +int +client_next_game( tree_t * restrict ptree, const char *str_addr, int iport ) +{ + int iret; + int my_turn; + const char *str_name1, *str_name2; + char buf1[SIZE_PLAYERNAME], buf2[SIZE_PLAYERNAME]; + + for ( ;; ) { + + str_buffer_cmdline[0] = '\0'; + sckt_csa = sckt_connect( str_addr, iport ); + if ( sckt_csa == SCKT_NULL ) { return -2; } + + str_name1 = str_name2 = NULL; + iret = sckt_out( sckt_csa, "LOGIN %s %s\n", + client_str_id, client_str_pwd ); + if ( iret < 0 ) { return iret; } + + Out( "wait for next game-conditions...\n" ); + + my_turn = black; + for ( ;; ) { + + iret = next_cmdline( 1 ); + if ( iret < 0 ) { return iret; } + else if ( game_status & flag_quit ) { return 1; } + + if ( ! strcmp( str_cmdline, "END Game_Summary" ) ) { break; } + else if ( ! strcmp( str_cmdline, "Your_Turn:-" ) ) { my_turn = white; } + else if ( ! memcmp( str_cmdline, "Name+:", 6 ) ) + { + strncpy( buf1, str_cmdline+6, SIZE_PLAYERNAME-1 ); + buf1[SIZE_PLAYERNAME-1] = '\0'; + str_name1 = buf1; + } + else if ( ! memcmp( str_cmdline, "Name-:", 6 ) ) + { + strncpy( buf2, str_cmdline+6, SIZE_PLAYERNAME-1 ); + buf2[SIZE_PLAYERNAME-1] = '\0'; + str_name2 = buf2; + } + } + + iret = sckt_out( sckt_csa, "AGREE\n" ); + if ( iret < 0 ) { return -2; } + + iret = next_cmdline( 1 ); + if ( iret < 0 ) { return iret; } + else if ( game_status & flag_quit ) { return 1; } + + if ( ! memcmp( str_cmdline, "REJECT:", 7 ) ) + { + ShutdownClient; + continue; + } + else if ( ! memcmp( str_cmdline, "START:", 6 ) ) { break; } + + str_error = str_server_err; + return -2; + } + + if ( ini_game( ptree, &min_posi_no_handicap, flag_history, + str_name1, str_name2 ) < 0 ) + { + return -1; + } + + if ( get_elapsed( &time_turn_start ) < 0 ) { return -1; } + + client_turn = my_turn; + client_ngame += 1; + + Out( "Game Conditions (%dth):\n", client_ngame ); + Out( " my turn:%c\n", ach_turn[my_turn] ); + + if ( my_turn == root_turn ) + { + iret = com_turn_start( ptree, 0 ); + if ( iret < 0 ) { return iret; } + } + + return 1; +} +#endif + + +#if defined(CSA_LAN) || defined(MNJ_LAN) +sckt_t +sckt_connect( const char *str_addr, int iport ) +{ + struct hostent *phe; + struct sockaddr_in sin; + sckt_t sd; + u_long ul_addr; + +#if defined(_WIN32) + { + WSADATA wsaData; + if ( WSAStartup( MAKEWORD(1,1), &wsaData ) ) + { + str_error = str_WSAError( "WSAStartup() failed." ); + return SCKT_NULL; + } + } +#endif + + ul_addr = inet_addr( str_addr ); + if ( ul_addr == INADDR_NONE ) + { + phe = gethostbyname( str_addr ); + if ( ! phe ) + { + str_error = str_WSAError( "gethostbyname() faild." ); +#if defined(_WIN32) + WSACleanup(); +#endif + return SCKT_NULL; + } + ul_addr = *( (u_long *)phe->h_addr_list[0] ); + } + + sd = socket( AF_INET, SOCK_STREAM, 0 ); + if ( sd == SCKT_NULL ) + { + str_error = str_WSAError( "socket() faild." ); +#if defined(_WIN32) + WSACleanup(); +#endif + return SCKT_NULL; + } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ul_addr; + sin.sin_port = htons( (u_short)iport ); + if ( connect( sd, (struct sockaddr *)&sin, sizeof(sin) ) == SOCKET_ERROR ) + { + str_error = str_WSAError( "connect() faild." ); +#if defined(_WIN32) + WSACleanup(); +#endif + return SCKT_NULL; + } + + return sd; +} + + +int +sckt_shutdown( sckt_t sd ) +{ + int iret; + + if ( sd == SCKT_NULL ) { return 1; } + Out( "shut down connection\n" ); + +# if defined(_WIN32) + if ( shutdown( sd, SD_SEND ) == SOCKET_ERROR ) + { + str_error = str_WSAError( "shutdown() faild." ); + WSACleanup(); + return -2; + } + + for ( ;; ) { + iret = recv( sd, str_message, SIZE_MESSAGE, 0 ); + if ( iret == SOCKET_ERROR ) + { + str_error = str_WSAError( "recv() failed." ); + WSACleanup(); + return -2; + } + else if ( ! iret ) { break; } + } + + if ( closesocket( sd ) == SOCKET_ERROR ) + { + str_error = str_WSAError( "closesocket() failed." ); + WSACleanup(); + return -2; + } + + if ( WSACleanup() == SOCKET_ERROR ) + { + str_error = str_WSAError( "WSACleanup() faild." ); + return -2; + } + + return 1; + +# else + if ( shutdown( sd, SHUT_RD ) == -1 ) + { + str_error = "shutdown() faild."; + return -2; + } + + for ( ;; ) { + iret = (int)recv( sd, str_message, SIZE_MESSAGE, 0 ); + if ( iret == -1 ) + { + str_error = "recv() failed."; + return -2; + } + else if ( ! iret ) { break; } + } + + if ( close( sd ) == -1 ) + { + str_error = "close() failed."; + return -2; + } + + return 1; +# endif +} + + +int +sckt_check( sckt_t sd ) +{ + struct timeval tv; + fd_set readfds; + int iret; + + tv.tv_sec = tv.tv_usec = 0; + + FD_ZERO( &readfds ); +# if defined(_MSC_VER) +# pragma warning(disable:4127) +# endif + FD_SET( sd, &readfds ); +# if defined(_MSC_VER) +# pragma warning(default:4127) +# endif + + iret = select( (int)sd+1, &readfds, NULL, NULL, &tv ); + if ( iret == SOCKET_ERROR ) + { + str_error = str_WSAError( "select() with a socket connected failed." ); + return -2; + } + + return iret; +} + + +int +sckt_in( sckt_t sd, char *str, int n ) +{ + struct timeval tv; + fd_set readfds; + int iret; + + for ( ;; ) { + Out( "wait for a message ... " ); + + tv.tv_sec = SEC_KEEP_ALIVE; + tv.tv_usec = 0; + FD_ZERO( &readfds ); +# if defined(_MSC_VER) +# pragma warning(disable:4127) +# endif + FD_SET( sd, &readfds ); +# if defined(_MSC_VER) +# pragma warning(default:4127) +# endif + + iret = select( (int)sd+1, &readfds, NULL, NULL, &tv ); + if ( iret == SOCKET_ERROR ) + { + str_error = str_WSAError( "select() with a socket connected failed." ); + return -1; + } + if ( iret ) + { + Out( "done.\n" ); + break; + } + Out( "time out.\n" ); + + if ( sckt_out( sd, "\n" ) == SOCKET_ERROR ) + { + str_error = str_WSAError( "send() failed." ); + return -1; + } + } + + iret = recv( sd, str, n-1, 0 ); + if ( iret == SOCKET_ERROR ) + { + str_error = str_WSAError( "recv() failed." ); + return -1; + } + *( str + iret ) = '\0'; + + if ( ! iret ) + { + Out( "Connection closed.\n" ); + return 0; + } + + Out( "%s[END]\n", str ); + + return iret; +} + + +int +sckt_out( sckt_t sd, const char *fmt, ... ) +{ + int nch; + char buf[256]; + va_list arg; + + va_start( arg, fmt ); + nch = vsnprintf( buf, 256, fmt, arg ); + va_end( arg ); + if ( nch >= 256 || nch < 0 ) + { + str_error = "buffer overflow at sckt_out()"; + return -1; + } + + Out( "- now sending the message: %s", buf ); + + if ( send( sd, buf, nch, 0 ) == SOCKET_ERROR ) + { + str_error = str_WSAError( "send() failed." ); + return -1; + } + + return get_elapsed( &time_last_send ); +} +#endif /* CSA_LAN || MNJ_LAN */ + diff --git a/search.c b/search.c new file mode 100644 index 0000000..d7ca199 --- /dev/null +++ b/search.c @@ -0,0 +1,1411 @@ +#include +#include +#include +#include +#include "shogi.h" + +static void hist_add( tree_t * restrict ptree, int ply ); +static void hist_good( tree_t * restrict ptree, unsigned int move, int ply, + int depth, int turn ); +static int detect_signals( tree_t * restrict ptree ); +static int detect_rep( tree_t * restrict ptree, int ply, int turn ); +static int rep_type( const tree_t * restrict ptree, int n, int i, int ply, + int turn ); + +/* #define DBG_SEARCH */ +#if defined(DBG_SEARCH) +# define DOut( ... ) if ( dbg_flag ) { out( __VA_ARGS__ ); } +#else +# define DOut( ... ) +#endif + +int +search( tree_t * restrict ptree, int alpha, int beta, int turn, int depth, + int ply, unsigned int state_node ) +{ + int value, alpha_old; + + if ( ! ptree->nsuc_check[ply] && depth < PLY_INC ) + { + return search_quies( ptree, alpha, beta, turn, ply, 1 ); + } + + +#if defined(DBG_SEARCH) + int dbg_flag = 0; + if ( iteration_depth == 3 && ply == 3 + && ! strcmp( str_CSA_move(ptree->current_move[1]), "7776FU" ) + && ! strcmp( str_CSA_move(ptree->current_move[2]), "3334FU" ) ) + { + dbg_flag = 1; + Out( "search start (d%.1f %" PRIu64 ")\n", + (double)depth / (double)PLY_INC, ptree->node_searched ); + } +#endif + + ptree->node_searched++; + + if ( ply >= PLY_MAX-1 ) + { + value = evaluate( ptree, ply, turn ); + if ( alpha < value && value < beta ) { pv_close( ptree, ply, no_rep ); } + MOVE_CURR = MOVE_NA; + return value; + } + + +#if ! defined(MINIMUM) + if ( ! ( game_status & flag_learning ) ) +#endif + { + /* check time and input */ +#if defined(TLP) + if ( ! ptree->tlp_id ) +#endif + if ( node_next_signal < ++node_last_check && detect_signals( ptree ) ) + { + root_abort = 1; + return 0; + } + + /* repetitions */ + ptree->nrep_tried++; + switch ( detect_rep( ptree, ply, turn ) ) + { + case black_superi_rep: + value = turn ? -score_inferior : score_inferior; + if ( alpha < value + && value < beta ) { pv_close( ptree, ply, black_superi_rep ); } + ptree->nsuperior_rep++; + MOVE_CURR = MOVE_NA; + return value; + + case white_superi_rep: + value = turn ? score_inferior : -score_inferior; + if ( alpha < value + && value < beta ) { pv_close( ptree, ply, white_superi_rep ); } + ptree->nsuperior_rep++; + MOVE_CURR = MOVE_NA; + return value; + + case four_fold_rep: + if ( alpha < score_draw + && score_draw < beta ) { pv_close( ptree, ply, four_fold_rep );} + ptree->nfour_fold_rep++; + MOVE_CURR = MOVE_NA; + return score_draw; + + case perpetual_check: + ptree->nperpetual_check++; + MOVE_CURR = MOVE_NA; + return score_foul; + + case perpetual_check2: + if ( ply > 4 ) + { + ptree->nperpetual_check++; + MOVE_CURR = MOVE_NA; + return -score_foul; + } + break; + } + + ptree->nreject_tried++; + if ( rejections_probe( ptree, turn, ply ) ) + { + ptree->nreject_done++; + if ( alpha < score_inferior && score_inferior < beta ) + { + pv_close( ptree, ply, turn ? white_superi_rep:black_superi_rep ); + } + MOVE_CURR = MOVE_NA; + return score_inferior; + } + } + + /* + no repetitions. worst situation of this node is checkmate, + and the best is to force checkmate within 1-ply. + */ + alpha_old = alpha; + value = - ( score_mate1ply + 2 - ply ); + if ( alpha < value ) + { + if ( beta <= value ) + { + MOVE_CURR = MOVE_NA; + return value; + } + alpha = value; + } + else { + value = score_mate1ply + 1 - ply; + if ( value <= alpha ) { return value; } + } + +#if defined(NO_NULL_PRUNE) + state_node &= ~node_do_null; +#endif + + ptree->amove_hash[ply] = 0; +#if ! defined(MINIMUM) + if ( ! ( game_status & flag_learning ) ) +#endif + { + /* probe the transposition table */ + int tv = hash_probe( ptree, ply, depth, turn, alpha, beta, state_node ); + switch ( tv ) + { + case value_exact: + if ( alpha < HASH_VALUE ) + { + if ( HASH_VALUE < beta ) { pv_close( ptree, ply, hash_hit ); } + else { + MOVE_CURR = ptree->amove_hash[ply]; + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + } + } + return HASH_VALUE; + + case value_lower: + MOVE_CURR = ptree->amove_hash[ply]; + assert( beta <= HASH_VALUE ); + assert( ! IsMove(MOVE_CURR) + || is_move_valid( ptree, MOVE_CURR, turn ) ); + if ( beta == alpha_old + 1 ) { return HASH_VALUE; } + break; + + case value_upper: + assert( HASH_VALUE <= alpha ); + if ( beta == alpha_old + 1 ) { return HASH_VALUE; } + break; + + default: + state_node = tv; + } + } + + DOut( "\nhash cut passed" ); + + if ( depth >= REJEC_MIN_DEPTH ) { add_rejections( ptree, turn, ply ); } + + + if ( ! ptree->nsuc_check[ply] ) + { + /* detect a move mates in 3-ply */ + if ( ( state_node & node_do_mate ) + && is_mate_in3ply( ptree, turn, ply ) ) + { + value = score_mate1ply + 1 - ply; + + hash_store( ptree, ply, depth, turn, value_exact, value, + MOVE_CURR, 0 ); + + if ( alpha < value + && value < beta ) { pv_close( ptree, ply, mate_search ); } + + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + + + /* null move pruning */ + if ( 2*PLY_INC <= depth + && ( state_node & node_do_null ) + && beta == alpha_old + 1 + && beta <= evaluate( ptree, ply, turn ) ) + { + int null_depth, nrep; + + null_depth = NullDepth(depth); + nrep = root_nrep + ply - 1; + + MOVE_CURR = MOVE_PASS; + ptree->move_last[ply] = ptree->move_last[ply-1]; + ptree->stand_pat[ply+1] = (short)( - ptree->stand_pat[ply] ); + ptree->nsuc_check[ply+1] = 0; + ptree->rep_board_list[nrep] = HASH_KEY; + ptree->rep_hand_list[nrep] = HAND_B; + ptree->null_pruning_tried++; + + value = -search( ptree, -beta, 1-beta, Flip(turn), null_depth, ply+1, + node_do_mate | node_do_recap | node_do_futile ); + if ( SEARCH_ABORT ) + { + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + return 0; + } + + if ( beta <= value ) + { + ptree->null_pruning_done++; + if ( null_depth < PLY_INC ) + { + hash_store( ptree, ply, depth, turn, value_lower, + value, MOVE_NA, state_node ); + } + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + + DOut( "\nnull move cut!\n" ); + + assert( ! IsMove(MOVE_CURR) + || is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + + DOut( "\nnull passed" ); + + if ( value == - ( score_mate1ply - ply ) ) + { + state_node |= node_mate_threat; + } + } + } + + /* recursive iterative-deepening for pv nodes */ + if ( ! ptree->amove_hash[ply] + && depth >= 3*PLY_INC + && ( ( alpha == root_alpha && beta == root_beta ) + || ( alpha == -root_beta && beta == -root_alpha ) ) ) + { + if ( depth >= REJEC_MIN_DEPTH ) { sub_rejections( ptree, turn, ply ); } + value = search( ptree, -score_bound, beta, turn, depth-2*PLY_INC, ply, + node_do_mate | node_do_recap ); + if ( SEARCH_ABORT ) { return 0; } + + if ( beta <= value && IsMove(MOVE_CURR) ) + { + ptree->amove_hash[ply] = MOVE_CURR; + } + else if ( value < beta && ply <= (int)ptree->pv[ply-1].length ) + { + assert( -score_bound < value ); + ptree->amove_hash[ply] = ptree->pv[ply-1].a[ply]; + } + assert( ! ptree->amove_hash[ply] + || is_move_valid( ptree, ptree->amove_hash[ply], turn ) ); + + if ( depth >= REJEC_MIN_DEPTH ) { add_rejections( ptree, turn, ply ); } + } + + { + int depth_reduced, first_move_expanded, new_depth, extension; + int state_node_new; + + ptree->move_last[ply] = ptree->move_last[ply-1]; + ptree->anext_move[ply].next_phase = next_move_hash; + ptree->hist_nmove[ply] = 0; + first_move_expanded = 0; + + evaluate( ptree, ply, turn ); + + /* expand all of off-springs */ + while ( ptree->nsuc_check[ply] + ? gen_next_evasion( ptree, ply, turn ) + : gen_next_move( ptree, ply, turn ) ) { + + DOut( "\nexpand %s (%" PRIu64 ")", + str_CSA_move(MOVE_CURR), ptree->node_searched ); + + ptree->nsuc_check[ply+1] = 0U; + state_node_new = ( node_do_mate | node_do_recap | node_do_null + | node_do_futile ); + extension = 0; + depth_reduced = 0; + + hist_add( ptree, ply ); + + /* decision of extensions */ + if ( turn ? is_move_check_w( ptree, MOVE_CURR ) + : is_move_check_b( ptree, MOVE_CURR ) ) + { + ptree->check_extension_done++; + ptree->nsuc_check[ply+1] + = (unsigned char)( ptree->nsuc_check[ply-1] + 1U ); + extension = EXT_CHECK; + } + else if ( ptree->nsuc_check[ply] + && ptree->move_last[ply] - ptree->move_last[ply-1] == 1 ) + { + ptree->onerp_extension_done++; + extension = EXT_ONEREP; + } + else if ( ! ptree->nsuc_check[ply] + && ( state_node & node_do_recap ) + && I2To(MOVE_CURR) == I2To(MOVE_LAST) + && ( MOVE_CURR == ptree->anext_move[ply].move_cap1 + || ( ( ptree->anext_move[ply].value_cap1 + < ( ptree->anext_move[ply].value_cap2 + + MT_CAP_PAWN ) ) + && MOVE_CURR == ptree->anext_move[ply].move_cap2 )) + && ( UToCap(MOVE_LAST) + || ( I2IsPromote(MOVE_LAST) + && I2PieceMove(MOVE_LAST) != silver ) ) ) + { + ptree->recap_extension_done++; + state_node_new = node_do_null | node_do_mate | node_do_futile; + if ( ! I2IsPromote(MOVE_CURR) + && I2PieceMove(MOVE_LAST) == UToCap(MOVE_LAST) ) + { + extension = EXT_RECAP2; + } + else { extension = EXT_RECAP1; } + } + + LimitExtension( extension, ply ); + + new_depth = depth + extension - PLY_INC; + + /* reductions */ + if ( PLY_INC <= new_depth + && ! ( state_node & node_mate_threat ) + && ! ptree->nsuc_check[ply] + && ! UToCap(MOVE_CURR) + && ! ( I2IsPromote(MOVE_CURR) && I2PieceMove(MOVE_CURR) != silver ) + && ptree->amove_hash[ply] != MOVE_CURR + && ptree->killers[ply].no1 != MOVE_CURR + && ptree->killers[ply].no2 != MOVE_CURR ) + { + unsigned int key = phash( MOVE_CURR, turn ); + unsigned int good = ptree->hist_good[key] + 1; + unsigned int triedx8 = ( ptree->hist_tried[key] + 2 ) * 8U; + + if ( beta != alpha_old + 1 ) { + + if ( good * 62U < triedx8 ) { depth_reduced = PLY_INC * 4/2; } + else if ( good * 46U < triedx8 ) { depth_reduced = PLY_INC * 3/2; } + else if ( good * 34U < triedx8 ) { depth_reduced = PLY_INC * 2/2; } + else if ( good * 19U < triedx8 ) { depth_reduced = PLY_INC * 1/2; } + + } else { + + if ( good * 62U < triedx8 ) { depth_reduced = PLY_INC * 4/2; } + else if ( good * 46U < triedx8 ) { depth_reduced = PLY_INC * 3/2; } + else if ( good * 30U < triedx8 ) { depth_reduced = PLY_INC * 2/2; } + else if ( good * 12U < triedx8 ) { depth_reduced = PLY_INC * 1/2; } + } + + new_depth -= depth_reduced; + } + + /* futility pruning */ + if ( ! ptree->nsuc_check[ply+1] + && ! ptree->nsuc_check[ply] + && new_depth < 3*PLY_INC ) + { + int diff = estimate_score_diff( ptree, MOVE_CURR, turn ); + int bound = alpha; + + if ( 2*PLY_INC <= new_depth ) { bound -= EFUTIL_MG2; } + else if ( 1*PLY_INC <= new_depth ) { bound -= EFUTIL_MG1; } + + if ( eval_max_score( ptree, MOVE_CURR, ptree->stand_pat[ply], + turn, diff ) <= bound ) + { + first_move_expanded += 1; + continue; + } + } + + DOut( ", futil passed" ); + + MakeMove( turn, MOVE_CURR, ply ); + if ( I2From(MOVE_CURR) < nsquare + && ! ptree->nsuc_check[ply] + && InCheck(turn) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + + if ( ! ptree->nsuc_check[ply+1] && ! ptree->nsuc_check[ply] ) + { + evaluate( ptree, ply+1, Flip(turn) ); + assert( ptree->stand_pat[ply] != score_bound ); + + /* futility pruning */ + if ( ( new_depth < PLY_INC && - ptree->stand_pat[ply+1] <= alpha ) + || ( new_depth < 2*PLY_INC + && - ptree->stand_pat[ply+1] <= alpha - EFUTIL_MG1 ) + || ( new_depth < 3*PLY_INC + && - ptree->stand_pat[ply+1] <= alpha - EFUTIL_MG2 ) ) + { + first_move_expanded += 1; + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + } + + if ( depth_reduced ) + { + value = -search( ptree, -alpha-1, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + if ( ! SEARCH_ABORT && alpha < value ) + { + new_depth += depth_reduced; + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, + ply+1, state_node_new ); + } + } + else if ( ! first_move_expanded ) + { + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, ply+1, + state_node_new ); + } + else { + value = -search( ptree, -alpha-1, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + if ( ! SEARCH_ABORT && alpha < value && beta != alpha+1 ) + { + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + } + } + + if ( SEARCH_ABORT ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + return 0; + } + + UnMakeMove( turn, MOVE_CURR, ply ); + + if ( alpha < value ) + { + if ( new_depth < PLY_INC + && ! ptree->nsuc_check[ply+1] + && ptree->stand_pat[ply] != score_bound ) + { + check_futile_score_quies( ptree, MOVE_CURR, + ptree->stand_pat[ply], + -ptree->stand_pat[ply+1], turn ); + } + + if ( beta <= value ) + { + DOut( ", beta cut (%" PRIu64 ")\n", ptree->node_searched ); + + hash_store( ptree, ply, depth, turn, value_lower, value, + MOVE_CURR, state_node ); + hist_good( ptree, MOVE_CURR, ply, depth, turn ); + + ptree->fail_high++; + if ( ! first_move_expanded ) { ptree->fail_high_first++; } + + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + } + if ( alpha < value ) { alpha = value; } + + first_move_expanded += 1; +#if defined(TLP) + if ( ! ( ptree->nsuc_check[ply] + && ptree->move_last[ply] - ptree->move_last[ply-1] < 4 ) + && tlp_idle + && ( ( iteration_depth < 11 && PLY_INC * 3 <= depth ) + || PLY_INC * 4 <= depth ) ) + { + ptree->tlp_beta = (short)beta; + ptree->tlp_best = (short)alpha; + ptree->tlp_depth = (unsigned char)depth; + ptree->tlp_state_node = (unsigned char)state_node; + ptree->tlp_turn = (char)turn; + ptree->tlp_ply = (char)ply; + if ( tlp_split( ptree ) ) + { + if ( SEARCH_ABORT ) { return 0; } + value = ptree->tlp_best; + if ( alpha < value ) + { + if ( beta <= value ) + { + if ( depth >= REJEC_MIN_DEPTH ) + { + sub_rejections( ptree, turn, ply ); + } + + hash_store( ptree, ply, depth, turn, value_lower, + value, MOVE_CURR, state_node ); + + hist_good( ptree, MOVE_CURR, ply, depth, turn ); + + ptree->fail_high++; + + assert( is_move_valid( ptree, MOVE_CURR, turn ) ); + return value; + } + alpha = value; + } + break; + } + } +#endif + } + + DOut( "\nall searched (%" PRIu64 ")\n", ptree->node_searched ); + + if ( depth >= REJEC_MIN_DEPTH ) { sub_rejections( ptree, turn, ply ); } + + if ( ! first_move_expanded ) + { +#if ! defined(MINIMUM) + if ( (int)I2From( ptree->current_move[ply-1] ) == Drop2From( pawn ) ) + { + out_warning( "A checkmate by dropping pawn!!" ); + } +#endif + if ( alpha != alpha_old ) { pv_close( ptree, ply, 0 ); } + return alpha; + } + + + if ( alpha <= - ( score_mate1ply + 2 - ply ) ) + { +#if ! defined(MINIMUM) + out_warning( "A node returns a value lower than mate." ); +#endif + if ( alpha_old < -score_inferior && -score_inferior < beta ) + { + pv_close( ptree, ply, turn ? black_superi_rep : white_superi_rep ); + } + MOVE_CURR = MOVE_NA; + return -score_inferior; + } + + if ( alpha != alpha_old ) + { + hist_good( ptree, ptree->pv[ply].a[ply], ply, depth, turn ); + + pv_copy( ptree, ply ); + +#if ! defined(MINIMUM) + if ( ! ( game_status & flag_learning ) ) +#endif + { + hash_store( ptree, ply, depth, turn, value_exact, alpha, + ptree->pv[ply].a[ply], state_node ); + } + } + else { + hash_store( ptree, ply, depth, turn, value_upper, alpha, MOVE_NA, + state_node ); + } + } + + return alpha; +} + + +#if defined(TLP) +int +tlp_search( tree_t * restrict ptree, int alpha, int beta, int turn, + int depth, int ply, unsigned int state_node ) +{ + int value, new_depth, extension, state_node_new, iret, depth_reduced; + int alpha_old; + + assert( depth >= 3*PLY_INC ); + + alpha_old = alpha; + + for ( ;; ) { + lock( & ptree->tlp_ptree_parent->tlp_lock ); + if ( ptree->nsuc_check[ply] ) + { + iret = gen_next_evasion( ptree->tlp_ptree_parent, ply, turn ); + } + else { iret = gen_next_move( ptree->tlp_ptree_parent, ply, turn ); } + MOVE_CURR = ptree->tlp_ptree_parent->current_move[ply]; + hist_add( ptree->tlp_ptree_parent, ply ); + unlock( & ptree->tlp_ptree_parent->tlp_lock ); + if ( ! iret ) { break; } + + ptree->nsuc_check[ply+1] = 0U; + state_node_new = ( node_do_mate | node_do_recap | node_do_null + | node_do_futile ); + extension = 0; + depth_reduced = 0; + + if ( turn ? is_move_check_w( ptree, MOVE_CURR ) + : is_move_check_b( ptree, MOVE_CURR ) ) + { + ptree->check_extension_done++; + ptree->nsuc_check[ply+1] + = (unsigned char)( ptree->nsuc_check[ply-1] + 1U ); + extension = EXT_CHECK; + } + else if ( ! ptree->nsuc_check[ply] + && ( state_node & node_do_recap ) + && I2To(MOVE_CURR) == I2To(MOVE_LAST) + && ( MOVE_CURR == ptree->anext_move[ply].move_cap1 + || ( ( ptree->anext_move[ply].value_cap1 + < ptree->anext_move[ply].value_cap2 + MT_CAP_PAWN ) + && MOVE_CURR == ptree->anext_move[ply].move_cap2 ) ) + && ( UToCap(MOVE_LAST) + || ( I2IsPromote(MOVE_LAST) + && I2PieceMove(MOVE_LAST) != silver ) ) ) + { + ptree->recap_extension_done++; + state_node_new = node_do_null | node_do_mate | node_do_futile; + if ( ! I2IsPromote(MOVE_CURR) + && I2PieceMove(MOVE_LAST) == UToCap(MOVE_LAST) ) + { + extension = EXT_RECAP2; + } + else { extension = EXT_RECAP1; } + } + + LimitExtension( extension, ply ); + + new_depth = depth + extension - PLY_INC; + + /* reductions */ + if ( ! ( state_node & node_mate_threat ) + && ! ptree->nsuc_check[ply] + && ! UToCap(MOVE_CURR) + && ! ( I2IsPromote(MOVE_CURR) && I2PieceMove(MOVE_CURR) != silver ) + && ptree->killers[ply].no1 != MOVE_CURR + && ptree->killers[ply].no2 != MOVE_CURR ) + { + unsigned int key = phash( MOVE_CURR, turn ); + unsigned int good = ptree->hist_good[key] + 1; + unsigned int triedx8 = ( ptree->hist_tried[key] + 2 ) * 8U; + + if ( beta != alpha_old + 1 ) { + + if ( good * 62U < triedx8 ) { depth_reduced = PLY_INC * 4/2; } + else if ( good * 46U < triedx8 ) { depth_reduced = PLY_INC * 3/2; } + else if ( good * 34U < triedx8 ) { depth_reduced = PLY_INC * 2/2; } + else if ( good * 19U < triedx8 ) { depth_reduced = PLY_INC * 1/2; } + + } else { + + if ( good * 62U < triedx8 ) { depth_reduced = PLY_INC * 4/2; } + else if ( good * 46U < triedx8 ) { depth_reduced = PLY_INC * 3/2; } + else if ( good * 30U < triedx8 ) { depth_reduced = PLY_INC * 2/2; } + else if ( good * 12U < triedx8 ) { depth_reduced = PLY_INC * 1/2; } + } + + new_depth -= depth_reduced; + } + + if ( ! ptree->nsuc_check[ply+1] + && ! ptree->nsuc_check[ply] + && new_depth < 3*PLY_INC ) + { + int diff = estimate_score_diff( ptree, MOVE_CURR, turn ); + int bound = alpha; + + if ( 2*PLY_INC <= new_depth ) { bound -= EFUTIL_MG2; } + else if ( 1*PLY_INC <= new_depth ) { bound -= EFUTIL_MG1; } + + if ( eval_max_score( ptree, MOVE_CURR, ptree->stand_pat[ply], + turn, diff ) <= bound ) + { + continue; + } + } + + MakeMove( turn, MOVE_CURR, ply ); + if ( I2From(MOVE_CURR) < nsquare + && ! ptree->nsuc_check[ply] + && InCheck(turn) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + + if ( ! ptree->nsuc_check[ply+1] && ! ptree->nsuc_check[ply] ) + { + evaluate( ptree, ply+1, Flip(turn) ); + assert( ptree->stand_pat[ply] != score_bound ); + + /* futility pruning */ + if ( ( new_depth < PLY_INC && - ptree->stand_pat[ply+1] <= alpha ) + || ( new_depth < 2*PLY_INC + && - ptree->stand_pat[ply+1] <= alpha - EFUTIL_MG1 ) + || ( new_depth < 3*PLY_INC + && - ptree->stand_pat[ply+1] <= alpha - EFUTIL_MG2 ) ) + { + UnMakeMove( turn, MOVE_CURR, ply ); + continue; + } + } + + value = -search( ptree, -alpha-1, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + if ( ! SEARCH_ABORT && alpha < value ) + { + if ( ! depth_reduced && beta != alpha+1 ) + { + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + } + else if ( depth_reduced ) + { + new_depth += depth_reduced; + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, + ply + 1, state_node_new ); + } + } + + UnMakeMove( turn, MOVE_CURR, ply ); + + if ( SEARCH_ABORT ) { return 0; } + + if ( alpha < value ) { + + alpha = value; + if ( beta <= value ) { + int num; + + lock( &tlp_lock ); + if ( ptree->tlp_abort ) + { + unlock( &tlp_lock ); + return 0; + } + tlp_nabort += 1; + + for ( num = 0; num < tlp_max; num++ ) + if ( ptree->tlp_ptree_parent->tlp_ptrees_sibling[num] + && num != ptree->tlp_id ) + tlp_set_abort( ptree->tlp_ptree_parent->tlp_ptrees_sibling[num] ); + + unlock( &tlp_lock ); + + return value; + } + } + } + + return alpha; +} +#endif /* TLP */ + + +static int +detect_signals( tree_t * restrict ptree ) +{ + unsigned int tnow, telapsed, tpondered, tsearched, tcount, tlimit, tmax; + unsigned int tmax_limit_count, tlimit_count, u; + int iret, easy_time, last_value, stable; + + if ( ! ( game_status & flag_nopeek ) ) + { + /* peek input-buffer to find a command */ + iret = next_cmdline( 0 ); + if ( iret == -1 ) + { + game_status |= flag_search_error; + return 1; + } + else if ( iret == -2 ) + { + out_warning( "%s", str_error ); + ShutdownClient; + } + else if ( game_status & flag_quit ) { return 1; } /* EOF */ + else if ( iret ) + { + /* a command is found */ + iret = procedure( ptree ); + if ( iret == -1 ) + { + game_status |= flag_search_error; + next_cmdline( 1 ); + return 1; + } + else if ( iret == -2 ) + { + out_warning( "%s", str_error ); + next_cmdline( 1 ); + ShutdownClient; + } + else if ( iret == 1 ) { next_cmdline( 1 ); } + + if ( game_status & ( flag_quit | flag_quit_ponder + | flag_move_now | flag_suspend ) ) + { + return 1; + } + } + } + + /* check conditions of search-abortion, and obtain tnow and elapsed */ + if ( node_limit <= ptree->node_searched ) { return 1; } + + if ( get_elapsed( &tnow ) < 0 ) + { + game_status |= flag_search_error; + return 1; + } + + /* keep my connection alive */ +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL + && SEC_KEEP_ALIVE * 1000U < tnow - time_last_send + && sckt_out( sckt_csa, "\n" ) < 0 ) + { + game_status |= flag_search_error; + return 1; + } +#endif + +#if defined(MNJ_LAN) + if ( sckt_mnj != SCKT_NULL && 500U < tnow - time_last_send ) + { + uint64_t nodes; + +# if defined(TLP) + lock( &tlp_lock ); + nodes = tlp_count_node( tlp_atree_work ); + unlock( &tlp_lock ); +# else + nodes = ptree->node_searched; +# endif + + if ( sckt_out( sckt_mnj, "pid=%d n=%" PRIu64 "\n", mnj_posi_id, + nodes ) < 0 ) + { + game_status |= flag_search_error; + return 1; + } + } +#endif + + /* shortening the time limit by depth */ + if ( time_limit != UINT_MAX + && sec_limit_depth < PLY_MAX + && iteration_depth + 10 >= (int)sec_limit_depth ) + { + if ( iteration_depth + 0 >= (int)sec_limit_depth ) { u = 1U; } + else if ( iteration_depth + 1 >= (int)sec_limit_depth ) { u = 3U; } + else if ( iteration_depth + 2 >= (int)sec_limit_depth ) { u = 7U; } + else if ( iteration_depth + 3 >= (int)sec_limit_depth ) { u = 15U; } + else if ( iteration_depth + 4 >= (int)sec_limit_depth ) { u = 31U; } + else if ( iteration_depth + 5 >= (int)sec_limit_depth ) { u = 63U; } + else if ( iteration_depth + 6 >= (int)sec_limit_depth ) { u = 127U; } + else if ( iteration_depth + 7 >= (int)sec_limit_depth ) { u = 255U; } + else if ( iteration_depth + 8 >= (int)sec_limit_depth ) { u = 511U; } + else if ( iteration_depth + 9 >= (int)sec_limit_depth ) { u = 1023U; } + else { u = 2047U; } + + tlimit = u * 1000U + 1000U - time_response; + tmax = u * 5000U + 1000U - time_response; + if ( tlimit > time_limit ) { tlimit = time_limit; } + if ( tmax > time_max_limit ) { tmax = time_max_limit; } + } + else { + tlimit = time_limit; + tmax = time_max_limit; + } + + telapsed = tnow - time_turn_start; + tpondered = time_turn_start - time_start; + tsearched = tnow - time_start; + easy_time = 0; + last_value = root_turn ? -last_root_value : last_root_value; + stable = ( tlimit != UINT_MAX + && ( ( root_alpha == root_value && ! root_nfail_low ) + || last_pv.type == four_fold_rep + || ( root_nfail_high + && root_value + MT_CAP_DRAGON/8 >= last_value ) ) ); + + if ( tlimit != tmax ) + { + tcount = tsearched; + tmax_limit_count = tmax; + tlimit_count = tlimit; + } + else { + tcount = telapsed; + tmax_limit_count = tmax + tpondered; + tlimit_count = tlimit + tpondered; + } + + if ( ! ( game_status & flag_pondering ) + && root_nmove == 1 + && telapsed > 2000U - time_response ) { return 1; } + + if ( tcount > tmax ) { return 1; } + + if ( stable && tcount > tlimit ) { return 1; } + + if ( stable + && ( tmax_limit_count * 3U < ( time_last_search - time_start ) * 5U ) ) + { +#if defined(DBG_EASY) + if ( ! easy_move ) + { + Out( " The search can be terminated " + "before it reaches time-limit.\n" ); + easy_move = ptree->pv[0].a[1]; + } +#else + Out( " The search is terminated before it reaches time-limit.\n" ); + return 1; +#endif + } + + if ( stable + && easy_abs > abs( last_value ) + && easy_min < last_value - easy_value + && easy_max > last_value - easy_value ) + { + u = tlimit_count / 5U; + if ( u < tpondered ) { ; } + else if ( u - tpondered < 2000U - time_response ) + { + u = tpondered + 2000U - time_response; + } + else { + u = ( ( u - tpondered ) / 1000U + 1U ) * 1000U + + tpondered - time_response; + } + + if ( tsearched > u ) + { + Out( " The root move %s counted as easy!!\n", + str_CSA_move(root_move_list[0].move) ); +#if defined(DBG_EASY) + easy_move = ptree->pv[0].a[1]; + easy_abs = 0; +#else + return 1; +#endif + } + else if ( tsearched + 500U > u ) { easy_time = 1; } + } + + /* renew node_per_second */ + { + double dn, dd; + + dn = (double)node_last_check * 1000.0; + dd = (double)( tnow - time_last_check ) + 0.1; + u = (unsigned int)( dn / dd ); + if ( node_per_second > u * 2U ) { node_per_second /= 2U; } + else if ( node_per_second < u / 2U ) { node_per_second *= 2U; } + else { node_per_second = u; } + } + + /* renew node_next_signal */ + if ( ! ( game_status & flag_pondering ) && root_nmove == 1 ) + { + u = 2000U - time_response; + } + else { u = tlimit; } + + if ( ! ( game_status & ( flag_pondering | flag_puzzling ) ) + && ! easy_time + && u > tcount + 500U ) + { + node_next_signal = node_per_second / 4U; + } + else { node_next_signal = node_per_second / 32U; } + + /* renew time_last_check and node_last_check */ + time_last_check = tnow; + node_last_check = 0; + + + return 0; +} + + +int +is_move_check_b( const tree_t * restrict ptree, unsigned int move ) +{ + const int from = (int)I2From(move); + const int to = (int)I2To(move); + int is_check, ipiece_move, idirec; + bitboard_t bb; + + is_check = 0; + + if ( from >= nsquare ) { ipiece_move = From2Drop(from); } + else { + ipiece_move = (int)I2PieceMove(move); + if ( I2IsPromote(move) ) { ipiece_move += promote; } + + idirec = (int)adirec[SQ_WKING][from]; + if ( idirec && idirec != (int)adirec[SQ_WKING][to] + && is_pinned_on_white_king( ptree, from, idirec ) ) + { + is_check = 1; + goto end; + } + } + + switch ( ipiece_move ) + { + case pawn: + if ( BOARD[to-nfile] == -king ) { is_check = 1; } + break; + + case lance: + AttackBLance( bb, to ); + if ( BBContract( bb, BB_WKING ) ) { is_check = 1; } + break; + + case knight: + if ( BBContract(abb_b_knight_attacks[to],BB_WKING) ) { is_check = 1; } + break; + + case silver: + if ( BBContract(abb_b_silver_attacks[to],BB_WKING) ) { is_check = 1; } + break; + + case gold: case pro_pawn: + case pro_lance: case pro_knight: + case pro_silver: + if ( BBContract(abb_b_gold_attacks[to],BB_WKING) ) { is_check = 1; } + break; + + case bishop: + AttackBishop( bb, to ); + if ( BBContract( bb, BB_WKING ) ) { is_check = 1; } + break; + + case rook: + AttackRook( bb, to ); + if ( BBContract( bb, BB_WKING ) ) { is_check = 1; } + break; + + case king: + break; + + case horse: + AttackHorse( bb, to ); + if ( BBContract( bb, BB_WKING ) ) { is_check = 1; } + break; + + default: + assert( ipiece_move == dragon ); + AttackDragon( bb, to ); + if ( BBContract( bb, BB_WKING ) ) { is_check = 1; } + break; + } + + end: + return is_check; +} + + +int +is_move_check_w( const tree_t * restrict ptree, unsigned int move ) +{ + const int from = (int)I2From(move); + const int to = (int)I2To(move); + int is_check, ipiece_move, idirec; + bitboard_t bb; + + is_check = 0; + + if ( from >= nsquare ) { ipiece_move = From2Drop(from); } + else { + ipiece_move = (int)I2PieceMove(move); + if ( I2IsPromote(move) ) { ipiece_move += promote; } + + idirec = (int)adirec[SQ_BKING][from]; + if ( idirec && idirec != (int)adirec[SQ_BKING][to] + && is_pinned_on_black_king( ptree, from, idirec ) ) + { + is_check = 1; + goto end; + } + } + + switch ( ipiece_move ) + { + case pawn: + if ( BOARD[to+nfile] == king ) { is_check = 1; } + break; + + case lance: + AttackWLance( bb, to ); + if ( BBContract( bb, BB_BKING ) ) { is_check = 1; } + break; + + case knight: + if ( BBContract(abb_w_knight_attacks[to],BB_BKING) ) { is_check = 1; } + break; + + case silver: + if ( BBContract(abb_w_silver_attacks[to],BB_BKING) ) { is_check = 1; } + break; + + case gold: case pro_pawn: + case pro_lance: case pro_knight: + case pro_silver: + if ( BBContract(abb_w_gold_attacks[to],BB_BKING) ) { is_check = 1; } + break; + + case bishop: + AttackBishop( bb, to ); + if ( BBContract( bb, BB_BKING ) ) { is_check = 1; } + break; + + case rook: + AttackRook( bb, to ); + if ( BBContract( bb, BB_BKING ) ) { is_check = 1; } + break; + + case king: + break; + + case horse: + AttackHorse( bb, to ); + if ( BBContract( bb, BB_BKING ) ) { is_check = 1; } + break; + + default: + assert( ipiece_move == dragon ); + AttackDragon( bb, to ); + if ( BBContract( bb, BB_BKING ) ) { is_check = 1; } + break; + } + + end: + return is_check; +} + + +void +pv_close( tree_t * restrict ptree, int ply, int type ) +{ + ptree->pv[ply-1].a[ply-1] = (ptree)->current_move[ply-1]; + ptree->pv[ply-1].length = (unsigned char)(ply-1); + ptree->pv[ply-1].type = (unsigned char)type; + ptree->pv[ply-1].depth = (unsigned char)iteration_depth; +} + + +void +pv_copy( tree_t * restrict ptree, int ply ) +{ + memcpy( &(ptree->pv[ply-1].a[ply]), &(ptree->pv[ply].a[ply]), + ( ptree->pv[ply].length-ply+1 ) * sizeof(unsigned int) ); + ptree->pv[ply-1].type = ptree->pv[ply].type; + ptree->pv[ply-1].length = ptree->pv[ply].length; + ptree->pv[ply-1].depth = ptree->pv[ply].depth; + ptree->pv[ply-1].a[ply-1] = ptree->current_move[ply-1]; +} + + +static int +detect_rep( tree_t * restrict ptree, int ply, int turn ) +{ + if ( ply < 4 ) { return detect_repetition( ptree, ply, turn, 2 ); } + else { + int n, i, imin, iret; + + n = root_nrep + ply - 1; + imin = n - REP_MAX_PLY; + if ( imin < 0 ) { imin = 0; } + + for ( i = n-2; i >= imin; i-- ) + if ( ptree->rep_board_list[i] == HASH_KEY ) + { + iret = rep_type( ptree, n, i, ply, turn ); + if ( iret ) { return iret; } + } + } + + return no_rep; +} + + +static int +rep_type( const tree_t * restrict ptree, int n, int i, int ply, int turn ) +{ + const unsigned int hand1 = HAND_B; + const unsigned int hand2 = ptree->rep_hand_list[i]; + + if ( (n-i) & 1 ) + { + if ( turn ) + { + if ( is_hand_eq_supe( hand2, hand1 ) ) { return white_superi_rep; } + } + else if ( is_hand_eq_supe( hand1, hand2 ) ) { return black_superi_rep; } + } + else if ( hand1 == hand2 ) + { + const int ncheck = (int)ptree->nsuc_check[ply]; + const int nchecked = (int)ptree->nsuc_check[ply-1]; + + if ( ncheck * 2 - 2 >= n-i ) { return perpetual_check; } + else if ( nchecked * 2 >= n-i ) { return perpetual_check2; } + else { return four_fold_rep; } + } + else if ( is_hand_eq_supe( hand1, hand2 ) ) { return black_superi_rep; } + else if ( is_hand_eq_supe( hand2, hand1 ) ) { return white_superi_rep; } + + return no_rep; +} + + +static void +hist_add( tree_t * restrict ptree, int ply ) +{ + if ( ptree->nsuc_check[ply] ) { return; } + if ( UToCap(MOVE_CURR) ) { return; } + if ( I2IsPromote(MOVE_CURR) + && I2PieceMove(MOVE_CURR) != silver ) { return; } + + assert( ptree->hist_nmove[ply] < MAX_LEGAL_MOVES ); + ptree->hist_move[ply][ ptree->hist_nmove[ply]++ ] = MOVE_CURR; +} + + +static void +hist_good( tree_t * restrict ptree, unsigned int move_good, int ply, + int depth, int turn ) +{ + unsigned int key, move; + int i, n, value, value_no1, value_no2; + + if ( ptree->nsuc_check[ply] ) { return; } + + value = p_value_ex[15+UToCap(move_good)]; + value_no1 = p_value_ex[15+BOARD[I2To(ptree->amove_killer[ply].no1)]]; + value_no2 = p_value_ex[15+BOARD[I2To(ptree->amove_killer[ply].no2)]]; + if ( move_good == ptree->anext_move[ply].move_cap1 ) + { + if ( ( ptree->anext_move[ply].phase_done & phase_killer1 ) + && UToFromToPromo(move_good) != ptree->amove_killer[ply].no1 ) + { + ptree->amove_killer[ply].no1_value + = ptree->anext_move[ply].value_cap1 - value - 1; + } + if ( ( ptree->anext_move[ply].phase_done & phase_killer2 ) + && UToFromToPromo(move_good) != ptree->amove_killer[ply].no2 ) + { + ptree->amove_killer[ply].no2_value + = ptree->anext_move[ply].value_cap1 - value - 2; + } + } + else if ( UToFromToPromo(move_good) == ptree->amove_killer[ply].no1 ) + { + if ( ( ptree->anext_move[ply].phase_done & phase_cap1 ) + && ( ptree->amove_killer[ply].no1_value + value + < ptree->anext_move[ply].value_cap1 + 1 ) ) + { + ptree->amove_killer[ply].no1_value + = ptree->anext_move[ply].value_cap1 - value + 1; + } + if ( ( ptree->anext_move[ply].phase_done & phase_killer2 ) + && ( ptree->amove_killer[ply].no1_value + value + < ptree->amove_killer[ply].no2_value + value_no2 + 1 ) ) + { + ptree->amove_killer[ply].no1_value + = ptree->amove_killer[ply].no2_value + value_no2 - value + 1; + } + } + else if ( UToFromToPromo(move_good) == ptree->amove_killer[ply].no2 ) + { + unsigned int uswap; + int iswap; + + if ( ( ptree->anext_move[ply].phase_done & phase_cap1 ) + && ( ptree->amove_killer[ply].no2_value + value + < ptree->anext_move[ply].value_cap1 + 1 ) ) + { + ptree->amove_killer[ply].no2_value + = ptree->anext_move[ply].value_cap1 - value + 1; + } + if ( ( ptree->anext_move[ply].phase_done & phase_killer1 ) + && ( ptree->amove_killer[ply].no2_value + value + < ptree->amove_killer[ply].no1_value + value_no1 + 1 ) ) + { + ptree->amove_killer[ply].no2_value + = ptree->amove_killer[ply].no1_value + value_no1 - value + 1; + } + + uswap = ptree->amove_killer[ply].no1; + ptree->amove_killer[ply].no1 = ptree->amove_killer[ply].no2; + ptree->amove_killer[ply].no2 = uswap; + + iswap = ptree->amove_killer[ply].no1_value; + ptree->amove_killer[ply].no1_value = ptree->amove_killer[ply].no2_value; + ptree->amove_killer[ply].no2_value = iswap; + } + else { + ptree->amove_killer[ply].no2 = ptree->amove_killer[ply].no1; + ptree->amove_killer[ply].no1 = UToFromToPromo(move_good); + + if ( ptree->anext_move[ply].phase_done & phase_killer1 ) + { + i = swap( ptree, move_good, -MT_CAP_ROOK, MT_CAP_ROOK, turn ); + i -= value + 1; + + if ( ptree->amove_killer[ply].no1_value > i ) + { + ptree->amove_killer[ply].no1_value = i; + } + } + ptree->amove_killer[ply].no2_value = ptree->amove_killer[ply].no1_value; + ptree->amove_killer[ply].no1_value + = ptree->anext_move[ply].value_cap1 - value + 1; + } + + + if ( UToCap(move_good) ) { return; } + if ( I2IsPromote(move_good) + && I2PieceMove(move_good) != silver ) { return; } + + if ( ptree->killers[ply].no1 != move_good ) + { + ptree->killers[ply].no2 = ptree->killers[ply].no1; + ptree->killers[ply].no1 = move_good; + } + + if ( depth < 1 ) { depth = 1; } + + n = ptree->hist_nmove[ply]; + for ( i = 0; i < n; i++ ) + { + move = ptree->hist_move[ply][i]; + assert( is_move_valid( ptree, move, turn ) ); + + key = phash( move, turn ); + if ( ptree->hist_tried[key] >= HIST_MAX ) + { + ptree->hist_good[key] /= 2U; + ptree->hist_tried[key] /= 2U; + } + + assert( ptree->hist_tried[key] < HIST_MAX ); + ptree->hist_tried[key] + = (unsigned short)( (int)ptree->hist_tried[key] + depth ); + } + + assert( is_move_valid( ptree, move_good, turn ) ); + key = phash( move_good, turn ); + + assert( ptree->hist_good[key] < HIST_MAX ); + ptree->hist_good[key] + = (unsigned short)( (int)ptree->hist_good[key] + depth ); +} diff --git a/searchr.c b/searchr.c new file mode 100644 index 0000000..a3aba06 --- /dev/null +++ b/searchr.c @@ -0,0 +1,740 @@ +#include +#include +#include +#include +#include "shogi.h" + +static int find_root_move( unsigned int move ); +static int save_result( tree_t * restrict ptree, int value, int beta, + int turn ); + +#if defined(MPV) +static int mpv_set_bound( int alpha ); +static int mpv_find_min( int *pnum ); +static int mpv_add_result( tree_t * restrict ptree, int value ); +static void mpv_sub_result( unsigned int move ); +static void mpv_out( tree_t * restrict ptree, int turn, unsigned int time ); +#endif + +#if defined(NO_STDOUT) && defined(NO_LOGGING) +# define NextRootMove(a,b) next_root_move(a) +static int next_root_move( tree_t * restrict ptree ); +#else +# define NextRootMove(a,b) next_root_move(a,b) +static int next_root_move( tree_t * restrict ptree, int turn ); +#endif + +int +searchr( tree_t * restrict ptree, int alpha, int beta, int turn, int depth ) +{ + uint64_t root_nodes_start; + int value, first_move_expanded, i; + int new_depth, extension, ply, state_node_new; +#if defined(MPV) + int bound = INT_MIN; +#endif + + first_move_expanded = 1; + ply = 1; + ptree->move_last[1] = ptree->move_last[0]; + + while( NextRootMove( ptree, turn ) ) + { + root_nodes_start = ptree->node_searched; + MakeMove( turn, MOVE_CURR, 1 ); + + assert( ! InCheck( turn ) ); + + state_node_new = ( node_do_mate | node_do_null | node_do_futile + | node_do_recap ); + if ( InCheck(Flip(turn)) ) + { + ptree->check_extension_done++; + ptree->nsuc_check[2] = (unsigned char)( ptree->nsuc_check[0] + 1U ); + extension = EXT_CHECK - PLY_INC; + } + else { + extension = - PLY_INC; + ptree->nsuc_check[2] = 0; + } + + if ( first_move_expanded ) + { +#if defined(MPV) + if ( root_mpv ) { bound = alpha; } +#endif + new_depth = depth + extension; + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, 2, + state_node_new ); + if ( root_abort ) + { + UnMakeMove( turn, MOVE_CURR, 1 ); + return 0; + } + + if ( value <= alpha ) + { + UnMakeMove( turn, MOVE_CURR, 1 ); + return value; + } + + first_move_expanded = 0; + } +#if defined(MPV) + else if ( root_mpv ) + { + bound = mpv_set_bound( alpha ); + if ( depth + extension >= PLY_INC || ptree->nsuc_check[2] ) + { + new_depth = depth + extension; + + value = -search( ptree, -bound-1, -bound, Flip(turn), new_depth, + 2, state_node_new ); + if ( ! root_abort && bound < value ) + { + new_depth = depth + extension; + value = -search( ptree, -beta, -bound, Flip(turn), new_depth, + 2, state_node_new ); + } + if ( root_abort ) + { + UnMakeMove( turn, MOVE_CURR, 1 ); + return 0; + } + } + else { + value = -search_quies( ptree, -beta, -bound, Flip(turn), 2, 1 ); + } + } +#endif /* MPV */ + else { + new_depth = depth + extension; + + value = -search( ptree, -alpha-1, -alpha, Flip(turn), new_depth, 2, + state_node_new ); + if ( root_abort ) + { + UnMakeMove( turn, MOVE_CURR, 1 ); + return 0; + } + if ( alpha < value ) + { + MnjOut( "pid=%d move=%s v=%dl n=% " PRIu64 " \n", + mnj_posi_id, str_CSA_move(MOVE_CURR), alpha+1, + ptree->node_searched ); + + new_depth = depth + extension; + easy_abs = 0; + value = -search( ptree, -beta, -alpha, Flip(turn), new_depth, + 2, state_node_new ); + if ( root_abort ) + { + const char *str; + double dvalue; + char ch; + + UnMakeMove( turn, MOVE_CURR, 1 ); + pv_close( ptree, 2, pv_fail_high ); + time_last_result = time_last_check; + time_last_eff_search = time_last_search; + root_value = alpha+1; + ptree->pv[0] = ptree->pv[1]; + if ( turn ) + { + dvalue = -(double)alpha; + ch = '-'; + } + else { + dvalue = alpha; + ch = '+'; + } + str = str_CSA_move_plus( ptree, MOVE_CURR, 1, turn ); + Out( " %7.2f 1.%c%s [%c0!]\n", + dvalue / 100.0, ch, str, ch ); + if ( game_status & flag_pondering ) + { + OutCsaShogi( "info%+.2f %c%s %c%s [%c0!]\n", + dvalue / 100.0, ach_turn[Flip(turn)], + str_CSA_move(ponder_move), + ch, str, ch ); + } + else { + OutCsaShogi( "info%+.2f %c%s [%c0!]\n", + dvalue / 100.0, ch, str, ch ); + } + return 0; + } + } + } + + UnMakeMove( turn, MOVE_CURR, 1 ); + + i = find_root_move( MOVE_CURR ); + root_move_list[i].nodes = ptree->node_searched - root_nodes_start; + +#if defined(MPV) + if ( root_mpv && value < beta ) + { + mpv_sub_result( MOVE_CURR ); + if ( bound < value && mpv_add_result( ptree, value ) < 0 ) + { + game_status |= flag_search_error; + return 1; + } + } +#endif + + if ( alpha < value ) + { + if ( save_result( ptree, value, beta, turn ) < 0 ) + { + game_status |= flag_search_error; + return 1; + } + if ( beta <= value ) + { + hash_store( ptree, 1, depth, turn, value_lower, value, + MOVE_CURR, 0 ); + return value; + } + alpha = value; + } + } + if ( root_abort ) { return 0; } + +#if ! defined(MINIMUM) + if ( ! ( game_status & flag_learning ) ) +#endif + { + hash_store( ptree, 1, depth, turn, value_exact, alpha, + ptree->pv[1].a[1], 0 ); + } + + return alpha; +} + + +void +out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time ) +{ + const char *str; + double dvalue; + int ply, tt, is_out; + + tt = turn; + is_out = ( iteration_depth > 3 || abs(value) > score_max_eval ) ? 1 : 0; + +#if defined(MPV) + if ( root_mpv ) { is_out = 0; } +#endif + + if ( is_out ) + { + str = str_time_symple( time ); + dvalue = (double)( turn ? -value : value ) / 100.0; + OutCsaShogi( "info%+.2f", dvalue ); + if ( game_status & flag_pondering ) + { + OutCsaShogi( " %c%s", ach_turn[Flip(turn)], + str_CSA_move(ponder_move) ); + } + + if ( ptree->pv[0].length ) + { + int i = find_root_move( ptree->pv[0].a[1] ); + if ( root_move_list[i].status & flag_first ) + { + Out( " %2d %6s %7.2f ", iteration_depth, str, dvalue ); + } + else { Out( " %6s %7.2f ", str, dvalue ); } + } + } + + for ( ply = 1; ply <= ptree->pv[0].length; ply++ ) + { + if ( is_out ) + { + if ( ply > 1 && ! ( (ply-1) % 4 ) ) + { + Out( "\n " ); + } + str = str_CSA_move_plus( ptree, ptree->pv[0].a[ply], ply, tt ); + OutCsaShogi( " %c%s", ach_turn[tt], str ); + Out( "%2d.%c%-11s", ply, ach_turn[tt], str ); + } + + MakeMove( tt, ptree->pv[0].a[ply], ply ); + tt = Flip(tt); + value = -value; + } + + if ( ptree->pv[0].type == hash_hit ) + { + int i, value_type; + + for ( ; ply < PLY_MAX; ply++ ) + { + ptree->amove_hash[ply] = 0; + value_type = hash_probe( ptree, ply, 0, tt, -score_bound, + score_bound, 0 ); + if ( ! ( value_type == value_exact + && value == HASH_VALUE + && is_move_valid( ptree, ptree->amove_hash[ply], tt ) ) ) + { + break; + } + ptree->pv[0].a[ply] = ptree->amove_hash[ply]; + for ( i = 1; i < ply; i++ ) + if ( ptree->pv[0].a[i] == ptree->pv[0].a[ply] ) { goto rep_esc; } + + if ( is_out ) + { + if ( ply > 1 && ! ( (ply-1) % 4 ) ) + { + Out( "\n " ); + } + str = str_CSA_move_plus( ptree, ptree->pv[0].a[ply], ply, + tt ); + OutCsaShogi( " %c%s", ach_turn[tt], str ); + Out( "%2d:%c%-11s", ply, ach_turn[tt], str ); + } + + MakeMove( tt, ptree->pv[0].a[ply], ply ); + if ( InCheck(tt) ) + { + UnMakeMove( tt, ptree->amove_hash[ply], ply ); + break; + } + ptree->pv[0].length++; + tt = Flip(tt); + value = -value; + } + } + rep_esc: + + if ( is_out && ptree->pv[0].type != no_rep ) + { + if ( (((ply-1) % 4) == 0) && (ply != 1) ) + { + Out( "\n " ); + } + str = NULL; + switch ( ptree->pv[0].type ) + { + case perpetual_check: str = "PER. CHECK"; break; + case four_fold_rep: str = "REPETITION"; break; + case black_superi_rep: + case white_superi_rep: str = "SUPERI. POSI."; break; + case prev_solution: str = "PREV. SEARCH"; break; + case hash_hit: str = "HASH HIT"; break; + case book_hit: str = "BOOK HIT"; break; + case pv_fail_high: str = "FAIL HIGH"; break; + case mate_search: str = "MATE SEARCH"; break; + } + if ( str != NULL ) { Out( " <%s>", str ); } + } + for ( ply--; ply >= 1; ply-- ) + { + tt = Flip(tt); + UnMakeMove( tt, ptree->pv[0].a[ply], ply ); + } + + if ( is_out ) + { + OutCsaShogi( "\n" ); + Out( "\n" ); + } +} + + +static int +save_result( tree_t * restrict ptree, int value, int beta, int turn ) +{ + root_move_t root_move_temp; + int i; + + if ( get_elapsed( &time_last_result ) < 0 ) { return -1; } + + i = find_root_move( ptree->current_move[1] ); + if ( i ) + { + root_move_temp = root_move_list[i]; + for ( ; i > 0; i-- ) { root_move_list[i] = root_move_list[i-1]; } + root_move_list[0] = root_move_temp; + } + + if ( beta <= value ) { pv_close( ptree, 2, pv_fail_high ); } + + if ( ptree->pv[0].a[1] != ptree->pv[1].a[1] ) + { + time_last_eff_search = time_last_search; + } + + ptree->pv[0] = ptree->pv[1]; + root_value = value; + + if ( value < beta ) + { + MnjOut( "pid=%d move=%s v=%de n=%" PRIu64 "\n", mnj_posi_id, + str_CSA_move(ptree->pv[1].a[1]), value, ptree->node_searched ); + out_pv( ptree, value, turn, time_last_result - time_start ); + } + + return 1; +} + + +static int +#if defined(NO_STDOUT) && defined(NO_LOGGING) +next_root_move( tree_t * restrict ptree ) +#else +next_root_move( tree_t * restrict ptree, int turn ) +#endif +{ + int i, n; + + n = root_nmove; + for ( i = 0; i < n; i++ ) + { + if ( root_move_list[i].status & flag_searched ) { continue; } + root_move_list[i].status |= flag_searched; + ptree->current_move[1] = root_move_list[i].move; + +#if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) ) + if ( iteration_depth > 5 ) + { + const char *str_move; + char str[9]; + /* check: does this code have small impact on performance? */ + str_move = str_CSA_move_plus( ptree, ptree->current_move[1], 1, + turn); + snprintf( str, 9, "%d/%d", i+1, root_nmove ); + if ( root_move_list[i].status & flag_first ) + { + Out( "(%2d) %7s* 1.%c%s \r", + iteration_depth, str, ach_turn[turn], str_move ); + } + else { + Out( " %7s* 1.%c%s \r", + str, ach_turn[turn], str_move ); + } + } +#endif + + return 1; + } + + return 0; +} + + +static int +find_root_move( unsigned int move ) +{ + int i, n; + + n = root_nmove; + for ( i = 0; i < n; i++ ) + if ( root_move_list[i].move == move ) { break; } + + return i; +} + + +#if defined(MPV) +static void +mpv_out( tree_t * restrict ptree, int turn, unsigned int time ) +{ + int mpv_out, ipv, best; + + best = (int)mpv_pv[0].a[0] - 32768; + mpv_out = ( iteration_depth > 3 || abs(best) > score_max_eval ) ? 1 : 0; + + for ( ipv = 0; mpv_pv[ipv].length; ipv++ ) + { + const char *str; + double dvalue; + int tt, is_out, value, ply; + + assert( ipv < mpv_num*2 ); + tt = turn; + value = (int)mpv_pv[ipv].a[0] - 32768; + if ( mpv_out && value > best - mpv_width && ipv < mpv_num ) + { + is_out = 1; + } + else { is_out = 0; } + + if ( is_out ) + { + dvalue = (double)( turn ? -value : value ) / 100.0; + if ( is_out && ! ipv ) { OutCsaShogi( "info" ); } + if ( is_out && ipv ) { OutCsaShogi( ":" ); } + + OutCsaShogi( "%+.2f", dvalue ); + if ( game_status & flag_pondering ) + { + OutCsaShogi( " %c%s", ach_turn[Flip(turn)], + str_CSA_move(ponder_move) ); + } + + str = str_time_symple( time ); + ply = mpv_pv[ipv].depth; + if ( ! ipv ) { Out( "o%2d %6s %7.2f ", ply, str, dvalue ); } + else { Out( " %2d %7.2f ", ply, dvalue ); } + } + + for ( ply = 1; ply <= mpv_pv[ipv].length; ply++ ) + { + if ( is_out ) + { + if ( ply > 1 && ! ( (ply-1) % 4 ) ) + { + Out( "\n " ); + } + str = str_CSA_move_plus( ptree, mpv_pv[ipv].a[ply], ply, tt ); + OutCsaShogi( " %c%s", ach_turn[tt], str ); + Out( "%2d.%c%-11s", ply, ach_turn[tt], str ); + } + + assert( is_move_valid( ptree, mpv_pv[ipv].a[ply], tt ) ); + MakeMove( tt, mpv_pv[ipv].a[ply], ply ); + tt = Flip(tt); + value = -value; + } + + if ( mpv_pv[ipv].type == hash_hit ) + { + int i, value_type; + + for ( ; ply < PLY_MAX; ply++ ) + { + ptree->amove_hash[ply] = 0; + value_type = hash_probe( ptree, ply, 0, tt, -score_bound, + score_bound, 0 ); + if ( ! ( value_type == value_exact + && value == HASH_VALUE + && is_move_valid(ptree,ptree->amove_hash[ply],tt) ) ) + { + break; + } + mpv_pv[ipv].a[ply] = ptree->amove_hash[ply]; + for ( i = 1; i < ply; i++ ) + if ( mpv_pv[ipv].a[i] + == mpv_pv[ipv].a[ply] ) { goto rep_esc; } + + if ( is_out ) + { + if ( ply > 1 && ! ( (ply-1) % 4 ) ) + { + Out( "\n " ); + } + str = str_CSA_move_plus( ptree, mpv_pv[ipv].a[ply], ply, + tt ); + OutCsaShogi( " %c%s", ach_turn[tt], str ); + Out( "%2d:%c%-11s", ply, ach_turn[tt], str ); + } + + MakeMove( tt, mpv_pv[ipv].a[ply], ply ); + if ( InCheck(tt) ) + { + UnMakeMove( tt, ptree->amove_hash[ply], ply ); + break; + } + mpv_pv[ipv].length++; + tt = Flip(tt); + value = -value; + } + } + rep_esc: + + if ( is_out && mpv_pv[ipv].type != no_rep ) + { + if ( (((ply-1) % 4) == 0) && (ply != 1) ) + { + Out( "\n " ); + } + str = NULL; + switch ( mpv_pv[ipv].type ) + { + case perpetual_check: str = "PER. CHECK"; break; + case four_fold_rep: str = "REPETITION"; break; + case black_superi_rep: + case white_superi_rep: str = "SUPERI. POSI."; break; + case prev_solution: str = "PREV. SEARCH"; break; + case hash_hit: str = "HASH HIT"; break; + case book_hit: str = "BOOK HIT"; break; + case pv_fail_high: str = "FAIL HIGH"; break; + case mate_search: str = "MATE SEARCH"; break; + } + if ( str != NULL ) { Out( " <%s>", str ); } + } + for ( ply--; ply >= 1; ply-- ) + { + tt = Flip(tt); + UnMakeMove( tt, mpv_pv[ipv].a[ply], ply ); + } + + if ( is_out ) { Out( "\n" ); } + } + + if ( mpv_out ) { OutCsaShogi( "\n" ); } +} + + +static void +mpv_sub_result( unsigned int move ) +{ + int i; + + for ( i = 0; mpv_pv[i].length; i++ ) + { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + assert( mpv_pv[i].depth <= iteration_depth ); + assert( -score_bound < (int)mpv_pv[i].a[0]-32768 ); + assert( (int)mpv_pv[i].a[0]-32768 < score_bound ); + + if ( mpv_pv[i].a[1] == move ) { break; } + } + + for ( ; mpv_pv[i].length; i++ ) + { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + assert( mpv_pv[i].depth <= iteration_depth ); + assert( -score_bound < (int)mpv_pv[i].a[0]-32768 ); + assert( (int)mpv_pv[i].a[0]-32768 < score_bound ); + + mpv_pv[i] = mpv_pv[i+1]; + } +} + + +static int +mpv_add_result( tree_t * restrict ptree, int value ) +{ + pv_t pv_tmp, pv; + unsigned int time; + int i, vmin, num; + + vmin = mpv_find_min( &num ); + assert( num <= mpv_num ); + assert( num < root_nmove ); + assert( -score_bound < value ); + assert( value < score_bound ); + + /* remove the weakest pv if all of slots are full */ + if ( num == mpv_num ) + { + for ( i = 0; mpv_pv[i].length; i++ ) + { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + assert( mpv_pv[i].depth <= iteration_depth ); + assert( -score_bound < (int)mpv_pv[i].a[0]-32768 ); + assert( (int)mpv_pv[i].a[0]-32768 < score_bound ); + + if ( mpv_pv[i].depth == iteration_depth + && mpv_pv[i].a[0] == (unsigned int)(vmin+32768) ) { break; } + } + assert( i != mpv_num*2 ); + assert( mpv_pv[i].length ); + do { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + mpv_pv[i] = mpv_pv[i+1]; + i++; + } while ( mpv_pv[i].length ); + } + + /* add a pv */ + for ( i = 0; mpv_pv[i].length; i++ ) + { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + assert( -score_bound < (int)mpv_pv[i].a[0]-32768 ); + assert( (int)mpv_pv[i].a[0]-32768 < score_bound ); + + if ( mpv_pv[i].a[0] < (unsigned int)(value+32768) ) { break; } + } + + pv = ptree->pv[1]; + pv.a[0] = (unsigned int)(value+32768); + do { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + pv_tmp = mpv_pv[i]; + mpv_pv[i] = pv; + pv = pv_tmp; + i += 1; + } while ( pv.length ); + + if ( get_elapsed( &time ) < 0 ) { return -1; } + + mpv_out( ptree, root_turn, time - time_start ); + + return 1; +} + + +static int +mpv_set_bound( int alpha ) +{ + int bound, num, value; + + bound = alpha - mpv_width; + if ( bound < -score_bound ) { bound = -score_bound; } + + value = mpv_find_min( &num ); + assert( num <= mpv_num ); + assert( num < root_nmove ); + assert( num ); + assert( -score_bound < value ); + assert( value < score_bound ); + if ( num == mpv_num && bound < value ) { bound = value; } + + return bound; +} + + +static int +mpv_find_min( int *pnum ) +{ + int i, num; + unsigned int a[ MPV_MAX_PV+1 ], u, utemp; + + a[0] = 0; + num = 0; + for ( i = 0; mpv_pv[i].length; i++ ) + { + assert( i < mpv_num*2 ); + assert( i < root_nmove*2 ); + assert( mpv_pv[i].depth <= iteration_depth ); + + if ( mpv_pv[i].depth == iteration_depth ) + { + u = mpv_pv[i].a[0]; + assert( -score_bound < (int)u-32768 ); + assert( (int)u-32768 < score_bound ); + + for ( num = 0; u <= a[num]; num++ ); + do { + assert( num < mpv_num ); + assert( num < root_nmove ); + utemp = a[num]; + a[num] = u; + u = utemp; + num += 1; + } while ( u ); + a[num] = 0; + } + } + + if ( pnum ) { *pnum = num; } + + if ( num ) { return (int)a[num-1] - 32768; } + + return 0; +} +#endif diff --git a/shogi.h b/shogi.h new file mode 100644 index 0000000..809e687 --- /dev/null +++ b/shogi.h @@ -0,0 +1,1444 @@ +#ifndef SHOGI_H +#define SHOGI_H + +#include +#include "param.h" + +#if defined(_WIN32) + +# include +# define CONV_CDECL __cdecl +# define SCKT_NULL INVALID_SOCKET +typedef SOCKET sckt_t; + +#else + +# include +# include +# define CONV_CDECL +# define SCKT_NULL -1 +# define SOCKET_ERROR -1 +typedef int sckt_t; + +#endif + +/* Microsoft C/C++ */ +#if defined(_MSC_VER) + +# define _CRT_DISABLE_PERFCRIT_LOCKS +# define UINT64_MAX ULLONG_MAX +# define PRIu64 "I64u" +# define PRIx64 "I64x" +# define UINT64_C(u) ( u ) + +# define restrict __restrict +# define strtok_r strtok_s +# define read _read +# define strncpy( dst, src, len ) strncpy_s( dst, len, src, _TRUNCATE ) +# define snprintf( buf, size, fmt, ... ) \ + _snprintf_s( buf, size, _TRUNCATE, fmt, __VA_ARGS__ ) +# define vsnprintf( buf, size, fmt, list ) \ + _vsnprintf_s( buf, size, _TRUNCATE, fmt, list ) +typedef unsigned __int64 uint64_t; +typedef volatile long lock_t; + +/* GNU C and Intel C/C++ on x86 and x86-64 */ +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +# include +# define restrict __restrict +typedef volatile int lock_t; + +/* other targets. */ +#else + +# include +typedef pthread_mutex_t lock_t; +extern unsigned char aifirst_one[512]; +extern unsigned char ailast_one[512]; + +#endif + +/* + #define BK_ULTRA_NARROW + #define BK_COM + #define BK_SMALL + #define NO_NULL_PRUNE + #define NO_STDOUT + #define DBG_EASY +*/ + +#if defined(CSASHOGI) +# define NO_STDOUT +# if ! defined(WIN32_PIPE) +# define WIN32_PIPE +# endif +#endif + +#if defined(TLP) +# define SHARE volatile +# define SEARCH_ABORT ( root_abort || ptree->tlp_abort ) +#else +# define SHARE +# define SEARCH_ABORT root_abort +#endif + +#define QUIES_PLY_LIMIT 7 +#define SHELL_H_LEN 7 +#define MAX_ANSWER 8 +#define PLY_INC 8 +#define PLY_MAX 48 +#define RAND_N 624 +#define TC_NMOVE 37U +#define SEC_MARGIN 15U +#define SEC_KEEP_ALIVE 180U +#define TIME_RESPONSE 200U +#define RESIGN_THRESHOLD ( ( MT_CAP_DRAGON * 5 ) / 8 ) +#define BNZ_VER "Feliz 0.0" + +#define REP_MAX_PLY 32 +#define REP_HIST_LEN 256 + +#define EHASH_MASK 0x3fffffU /* occupies 32MB */ + +#define MNJ_MASK 0xfffU + +#define HIST_SIZE 0x4000U +#define HIST_INVALID 0xffffU +#define HIST_MAX 0x1000U + +#define REJEC_MASK 0x0ffffU +#define REJEC_MIN_DEPTH ( ( PLY_INC * 5 ) ) + +#define EXT_RECAP1 ( ( PLY_INC * 1 ) / 4 ) +#define EXT_RECAP2 ( ( PLY_INC * 2 ) / 4 ) +#define EXT_ONEREP ( ( PLY_INC * 2 ) / 4 ) +#define EXT_CHECK ( ( PLY_INC * 4 ) / 4 ) + +#define EFUTIL_MG1 ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define EFUTIL_MG2 ( ( MT_CAP_DRAGON * 2 ) / 8 ) + +#define FMG_MG ( ( MT_CAP_DRAGON * 2 ) / 16 ) +#define FMG_MG_KING ( ( MT_CAP_DRAGON * 3 ) / 16 ) +#define FMG_MG_MT ( ( MT_CAP_DRAGON * 8 ) / 16 ) +#define FMG_MISC ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define FMG_CAP ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define FMG_DROP ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define FMG_MT ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define FMG_MISC_KING ( ( MT_CAP_DRAGON * 2 ) / 8 ) +#define FMG_CAP_KING ( ( MT_CAP_DRAGON * 2 ) / 8 ) + +#define HASH_REG_HIST_LEN 256 +#define HASH_REG_MINDIFF ( ( MT_CAP_DRAGON * 1 ) / 8 ) +#define HASH_REG_THRESHOLD ( ( MT_CAP_DRAGON * 8 ) / 8 ) + +#define FV_WINDOW 256 +#define FV_SCALE 32 +#define FV_PENALTY ( 0.2 / (double)FV_SCALE ) + +#define MPV_MAX_PV 16 + +#define TLP_MAX_THREADS 12 +#define TLP_NUM_WORK ( TLP_MAX_THREADS * 8 ) + +#define TIME_CHECK_MIN_NODE 10000U +#define TIME_CHECK_MAX_NODE 100000U + +#define SIZE_FILENAME 256 +#define SIZE_PLAYERNAME 256 +#define SIZE_MESSAGE 512 +#define SIZE_CMDLINE 512 +#define SIZE_CSALINE 512 +#define SIZE_CMDBUFFER 512 + +#define IsMove(move) ( (move) & 0xffffffU ) +#define MOVE_NA 0x00000000U +#define MOVE_PASS 0x01000000U +#define MOVE_PONDER_FAILED 0xfe000000U +#define MOVE_RESIGN 0xff000000U +#define MOVE_CHK_SET 0x80000000U +#define MOVE_CHK_CLEAR 0x40000000U + +#define MAX_LEGAL_MOVES 700 +#define MAX_LEGAL_EVASION 256 +#define MOVE_LIST_LEN 16384 + +#define MAX_SIZE_SECTION 0xffff +#define NUM_SECTION 0x4000 + +#define MATERIAL (ptree->posi.material) +#define HAND_B (ptree->posi.hand_black) +#define HAND_W (ptree->posi.hand_white) + +#define BB_BOCCUPY (ptree->posi.b_occupied) +#define BB_BTGOLD (ptree->posi.b_tgold) +#define BB_B_HDK (ptree->posi.b_hdk) +#define BB_B_BH (ptree->posi.b_bh) +#define BB_B_RD (ptree->posi.b_rd) +#define BB_BPAWN_ATK (ptree->posi.b_pawn_attacks) +#define BB_BPAWN (ptree->posi.b_pawn) +#define BB_BLANCE (ptree->posi.b_lance) +#define BB_BKNIGHT (ptree->posi.b_knight) +#define BB_BSILVER (ptree->posi.b_silver) +#define BB_BGOLD (ptree->posi.b_gold) +#define BB_BBISHOP (ptree->posi.b_bishop) +#define BB_BROOK (ptree->posi.b_rook) +#define BB_BKING (abb_mask[SQ_BKING]) +#define BB_BPRO_PAWN (ptree->posi.b_pro_pawn) +#define BB_BPRO_LANCE (ptree->posi.b_pro_lance) +#define BB_BPRO_KNIGHT (ptree->posi.b_pro_knight) +#define BB_BPRO_SILVER (ptree->posi.b_pro_silver) +#define BB_BHORSE (ptree->posi.b_horse) +#define BB_BDRAGON (ptree->posi.b_dragon) + +#define BB_WOCCUPY (ptree->posi.w_occupied) +#define BB_WTGOLD (ptree->posi.w_tgold) +#define BB_W_HDK (ptree->posi.w_hdk) +#define BB_W_BH (ptree->posi.w_bh) +#define BB_W_RD (ptree->posi.w_rd) +#define BB_WPAWN_ATK (ptree->posi.w_pawn_attacks) +#define BB_WPAWN (ptree->posi.w_pawn) +#define BB_WLANCE (ptree->posi.w_lance) +#define BB_WKNIGHT (ptree->posi.w_knight) +#define BB_WSILVER (ptree->posi.w_silver) +#define BB_WGOLD (ptree->posi.w_gold) +#define BB_WBISHOP (ptree->posi.w_bishop) +#define BB_WROOK (ptree->posi.w_rook) +#define BB_WKING (abb_mask[SQ_WKING]) +#define BB_WPRO_PAWN (ptree->posi.w_pro_pawn) +#define BB_WPRO_LANCE (ptree->posi.w_pro_lance) +#define BB_WPRO_KNIGHT (ptree->posi.w_pro_knight) +#define BB_WPRO_SILVER (ptree->posi.w_pro_silver) +#define BB_WHORSE (ptree->posi.w_horse) +#define BB_WDRAGON (ptree->posi.w_dragon) + +#define OCCUPIED_FILE (ptree->posi.occupied_rl90) +#define OCCUPIED_DIAG1 (ptree->posi.occupied_rr45) +#define OCCUPIED_DIAG2 (ptree->posi.occupied_rl45) +#define BOARD (ptree->posi.asquare) + +#define SQ_BKING (ptree->posi.isquare_b_king) +#define SQ_WKING (ptree->posi.isquare_w_king) + +#define HASH_KEY (ptree->posi.hash_key) +#define HASH_VALUE (ptree->sort_value[0]) +#define MOVE_CURR (ptree->current_move[ply]) +#define MOVE_LAST (ptree->current_move[ply-1]) + +#define NullDepth(d) ( (d) < PLY_INC*26/4 ? (d)-PLY_INC*12/4 : \ + ( (d) <= PLY_INC*30/4 ? PLY_INC*14/4 \ + : (d)-PLY_INC*16/4) ) + +#define LimitExtension(e,ply) if ( (e) && (ply) > 2 * iteration_depth ) { \ + if ( (ply) < 4 * iteration_depth ) { \ + e *= 4 * iteration_depth - (ply); \ + e /= 2 * iteration_depth; \ + } else { e = 0; } } + +#define Flip(turn) ((turn)^1) +#define Inv(sq) (nsquare-1-sq) +#define PcOnSq(k,i) pc_on_sq[k][(i)*((i)+3)/2] +#define PcPcOnSq(k,i,j) pc_on_sq[k][(i)*((i)+1)/2+(j)] + +/* + xxxxxxxx xxxxxxxx xxx11111 pawn + xxxxxxxx xxxxxxxx 111xxxxx lance + xxxxxxxx xxxxx111 xxxxxxxx knight + xxxxxxxx xx111xxx xxxxxxxx silver + xxxxxxx1 11xxxxxx xxxxxxxx gold + xxxxx11x xxxxxxxx xxxxxxxx bishop + xxx11xxx xxxxxxxx xxxxxxxx rook + */ +#define I2HandPawn(hand) (((hand) >> 0) & 0x1f) +#define I2HandLance(hand) (((hand) >> 5) & 0x07) +#define I2HandKnight(hand) (((hand) >> 8) & 0x07) +#define I2HandSilver(hand) (((hand) >> 11) & 0x07) +#define I2HandGold(hand) (((hand) >> 14) & 0x07) +#define I2HandBishop(hand) (((hand) >> 17) & 0x03) +#define I2HandRook(hand) ((hand) >> 19) +#define IsHandPawn(hand) ((hand) & 0x000001f) +#define IsHandLance(hand) ((hand) & 0x00000e0) +#define IsHandKnight(hand) ((hand) & 0x0000700) +#define IsHandSilver(hand) ((hand) & 0x0003800) +#define IsHandGold(hand) ((hand) & 0x001c000) +#define IsHandBishop(hand) ((hand) & 0x0060000) +#define IsHandRook(hand) ((hand) & 0x0180000) +/* + xxxxxxxx xxxxxxxx x1111111 destination + xxxxxxxx xx111111 1xxxxxxx starting square or drop piece+nsquare-1 + xxxxxxxx x1xxxxxx xxxxxxxx flag for promotion + xxxxx111 1xxxxxxx xxxxxxxx piece to move + x1111xxx xxxxxxxx xxxxxxxx captured piece + */ +#define To2Move(to) ((unsigned int)(to) << 0) +#define From2Move(from) ((unsigned int)(from) << 7) +#define Drop2Move(piece) ((nsquare-1+(piece)) << 7) +#define Drop2From(piece) (nsquare-1+(piece)) +#define FLAG_PROMO (1U << 14) +#define Piece2Move(piece) ((piece) << 15) +#define Cap2Move(piece) ((piece) << 19) +#define I2To(move) (((move) >> 0) & 0x007fU) +#define I2From(move) (((move) >> 7) & 0x007fU) +#define I2FromTo(move) (((move) >> 0) & 0x3fffU) +#define I2IsPromote(move) ((move) & FLAG_PROMO) +#define I2PieceMove(move) (((move) >> 15) & 0x000fU) +#define UToFromToPromo(u) ( (u) & 0x7ffffU ) +#define UToCap(u) (((u) >> 19) & 0x000fU) +#define From2Drop(from) ((from)-nsquare+1) + + +#define BBIni(bb) (bb).p[0] = (bb).p[1] = (bb).p[2] = 0 +#define BBToU(bb) ((bb).p[0] | (bb).p[1] | (bb).p[2]) +#define BBToUShift(bb) ((bb).p[0]<<2 | (bb).p[1]<<1 | (bb).p[2]) +#define PopuCount(bb) popu_count012( bb.p[0], bb.p[1], bb.p[2] ) +#define FirstOne(bb) first_one012( bb.p[0], bb.p[1], bb.p[2] ) +#define LastOne(bb) last_one210( bb.p[2], bb.p[1], bb.p[0] ) + +#define BBCmp(bb1,bb2) ( (bb1).p[0] != (bb2).p[0] \ + || (bb1).p[1] != (bb2).p[1] \ + || (bb1).p[2] != (bb2).p[2] ) + +#define BBNot(bb,bb1) (bb).p[0] = ~(bb1).p[0], \ + (bb).p[1] = ~(bb1).p[1], \ + (bb).p[2] = ~(bb1).p[2] + +#define BBOr(bb,bb1,bb2) (bb).p[0] = (bb1).p[0] | (bb2).p[0], \ + (bb).p[1] = (bb1).p[1] | (bb2).p[1], \ + (bb).p[2] = (bb1).p[2] | (bb2).p[2] + +#define BBAnd(bb,bb1,bb2) (bb).p[0] = (bb1).p[0] & (bb2).p[0], \ + (bb).p[1] = (bb1).p[1] & (bb2).p[1], \ + (bb).p[2] = (bb1).p[2] & (bb2).p[2] + +#define BBXor(bb,b1,b2) (bb).p[0] = (b1).p[0] ^ (b2).p[0], \ + (bb).p[1] = (b1).p[1] ^ (b2).p[1], \ + (bb).p[2] = (b1).p[2] ^ (b2).p[2] + +#define BBAndOr(bb,bb1,bb2) (bb).p[0] |= (bb1).p[0] & (bb2).p[0], \ + (bb).p[1] |= (bb1).p[1] & (bb2).p[1], \ + (bb).p[2] |= (bb1).p[2] & (bb2).p[2] + +#define BBNotAnd(bb,bb1) bb.p[0] &= ~bb1.p[0]; \ + bb.p[1] &= ~bb1.p[1]; \ + bb.p[2] &= ~bb1.p[2] + +#define BBContractShift(bb1,bb2) ( ( (bb1).p[0] & (bb2).p[0] ) << 2 \ + | ( (bb1).p[1] & (bb2).p[1] ) << 1 \ + | ( (bb1).p[2] & (bb2).p[2] ) ) + +#define BBContract(bb1,bb2) ( ( (bb1).p[0] & (bb2).p[0] ) \ + | ( (bb1).p[1] & (bb2).p[1] ) \ + | ( (bb1).p[2] & (bb2).p[2] ) ) + +#define Xor(i,bb) (bb).p[0] ^= abb_mask[i].p[0], \ + (bb).p[1] ^= abb_mask[i].p[1], \ + (bb).p[2] ^= abb_mask[i].p[2] + +#define XorFile(i,bb) (bb).p[0] ^= abb_mask_rl90[i].p[0], \ + (bb).p[1] ^= abb_mask_rl90[i].p[1], \ + (bb).p[2] ^= abb_mask_rl90[i].p[2] + +#define XorDiag1(i,bb) (bb).p[0] ^= abb_mask_rr45[i].p[0], \ + (bb).p[1] ^= abb_mask_rr45[i].p[1], \ + (bb).p[2] ^= abb_mask_rr45[i].p[2] + +#define XorDiag2(i,bb) (bb).p[0] ^= abb_mask_rl45[i].p[0], \ + (bb).p[1] ^= abb_mask_rl45[i].p[1], \ + (bb).p[2] ^= abb_mask_rl45[i].p[2] + +#define SetClear(bb) (bb).p[0] ^= (bb_set_clear.p[0]), \ + (bb).p[1] ^= (bb_set_clear.p[1]), \ + (bb).p[2] ^= (bb_set_clear.p[2]) + +#define SetClearFile(i1,i2,bb) \ + (bb).p[0] ^= ((abb_mask_rl90[i1].p[0])|(abb_mask_rl90[i2].p[0])), \ + (bb).p[1] ^= ((abb_mask_rl90[i1].p[1])|(abb_mask_rl90[i2].p[1])), \ + (bb).p[2] ^= ((abb_mask_rl90[i1].p[2])|(abb_mask_rl90[i2].p[2])) + +#define SetClearDiag1(i1,i2,bb) \ + (bb).p[0] ^= ((abb_mask_rr45[i1].p[0])|(abb_mask_rr45[i2].p[0])), \ + (bb).p[1] ^= ((abb_mask_rr45[i1].p[1])|(abb_mask_rr45[i2].p[1])), \ + (bb).p[2] ^= ((abb_mask_rr45[i1].p[2])|(abb_mask_rr45[i2].p[2])) + +#define SetClearDiag2(i1,i2,bb) \ + (bb).p[0] ^= ((abb_mask_rl45[i1].p[0])|(abb_mask_rl45[i2].p[0])), \ + (bb).p[1] ^= ((abb_mask_rl45[i1].p[1])|(abb_mask_rl45[i2].p[1])), \ + (bb).p[2] ^= ((abb_mask_rl45[i1].p[2])|(abb_mask_rl45[i2].p[2])) + +#define AttackFile(i) (abb_file_attacks[i] \ + [((ptree->posi.occupied_rl90.p[aslide[i].irl90]) \ + >> aslide[i].srl90) & 0x7f]) + +#define AttackRank(i) (ai_rook_attacks_r0[i] \ + [((ptree->posi.b_occupied.p[aslide[i].ir0] \ + |ptree->posi.w_occupied.p[aslide[i].ir0]) \ + >> aslide[i].sr0) & 0x7f ]) + +#define AttackDiag1(i) \ + (abb_bishop_attacks_rr45[i] \ + [((ptree->posi.occupied_rr45.p[aslide[i].irr45]) \ + >> aslide[i].srr45) & 0x7f]) + +#define AttackDiag2(i) \ + (abb_bishop_attacks_rl45[i] \ + [((ptree->posi.occupied_rl45.p[aslide[i].irl45]) \ + >> aslide[i].srl45) & 0x7f]) + +#define BishopAttack0(i) ( AttackDiag1(i).p[0] | AttackDiag2(i).p[0] ) +#define BishopAttack1(i) ( AttackDiag1(i).p[1] | AttackDiag2(i).p[1] ) +#define BishopAttack2(i) ( AttackDiag1(i).p[2] | AttackDiag2(i).p[2] ) +#define AttackBLance(bb,i) BBAnd( bb, abb_minus_rays[i], AttackFile(i) ) +#define AttackWLance(bb,i) BBAnd( bb, abb_plus_rays[i], AttackFile(i) ) +#define AttackHorse(bb,i) AttackBishop(bb,i); BBOr(bb,bb,abb_king_attacks[i]) +#define AttackDragon(bb,i) AttackRook(bb,i); BBOr(bb,bb,abb_king_attacks[i]) + +#define InCheck(turn) \ + ( (turn) ? is_white_attacked( ptree, SQ_WKING ) \ + : is_black_attacked( ptree, SQ_BKING ) ) + +#define MakeMove(turn,move,ply) \ + ( (turn) ? make_move_w( ptree, move, ply ) \ + : make_move_b( ptree, move, ply ) ) + +#define UnMakeMove(turn,move,ply) \ + ( (turn) ? unmake_move_w( ptree, move, ply ) \ + : unmake_move_b( ptree, move, ply ) ) + +#define IsMoveCheck( ptree, turn, move ) \ + ( (turn) ? is_move_check_w( ptree, move ) \ + : is_move_check_b( ptree, move ) ) + +#define GenCaptures(turn,pmove) ( (turn) ? w_gen_captures( ptree, pmove ) \ + : b_gen_captures( ptree, pmove ) ) + +#define GenNoCaptures(turn,pmove) \ + ( (turn) ? w_gen_nocaptures( ptree, pmove ) \ + : b_gen_nocaptures( ptree, pmove ) ) + +#define GenDrop(turn,pmove) ( (turn) ? w_gen_drop( ptree, pmove ) \ + : b_gen_drop( ptree, pmove ) ) + +#define GenCapNoProEx2(turn,pmove) \ + ( (turn) ? w_gen_cap_nopro_ex2( ptree, pmove ) \ + : b_gen_cap_nopro_ex2( ptree, pmove ) ) + +#define GenNoCapNoProEx2(turn,pmove) \ + ( (turn) ? w_gen_nocap_nopro_ex2( ptree, pmove ) \ + : b_gen_nocap_nopro_ex2( ptree, pmove ) ) + +#define GenEvasion(turn,pmove) \ + ( (turn) ? w_gen_evasion( ptree, pmove ) \ + : b_gen_evasion( ptree, pmove ) ) + +#define GenCheck(turn,pmove) \ + ( (turn) ? w_gen_checks( ptree, pmove ) \ + : b_gen_checks( ptree, pmove ) ) + +#define IsMateIn1Ply(turn) \ + ( (turn) ? is_w_mate_in_1ply( ptree ) \ + : is_b_mate_in_1ply( ptree ) ) + +#define IsDiscoverBK(from,to) \ + idirec = (int)adirec[SQ_BKING][from], \ + ( idirec && ( idirec!=(int)adirec[SQ_BKING][to] ) \ + && is_pinned_on_black_king( ptree, from, idirec ) ) + +#define IsDiscoverWK(from,to) \ + idirec = (int)adirec[SQ_WKING][from], \ + ( idirec && ( idirec!=(int)adirec[SQ_WKING][to] ) \ + && is_pinned_on_white_king( ptree, from, idirec ) ) +#define IsMateWPawnDrop(ptree,to) ( BOARD[(to)+9] == king \ + && is_mate_w_pawn_drop( ptree, to ) ) + +#define IsMateBPawnDrop(ptree,to) ( BOARD[(to)-9] == -king \ + && is_mate_b_pawn_drop( ptree, to ) ) + +enum { b0000, b0001, b0010, b0011, b0100, b0101, b0110, b0111, + b1000, b1001, b1010, b1011, b1100, b1101, b1110, b1111 }; + +enum { A9 = 0, B9, C9, D9, E9, F9, G9, H9, I9, + A8, B8, C8, D8, E8, F8, G8, H8, I8, + A7, B7, C7, D7, E7, F7, G7, H7, I7, + A6, B6, C6, D6, E6, F6, G6, H6, I6, + A5, B5, C5, D5, E5, F5, G5, H5, I5, + A4, B4, C4, D4, E4, F4, G4, H4, I4, + A3, B3, C3, D3, E3, F3, G3, H3, I3, + A2, B2, C2, D2, E2, F2, G2, H2, I2, + A1, B1, C1, D1, E1, F1, G1, H1, I1 }; + +enum { promote = 8, empty = 0, + pawn, lance, knight, silver, gold, bishop, rook, king, pro_pawn, + pro_lance, pro_knight, pro_silver, piece_null, horse, dragon }; + +enum { npawn_max = 18, nlance_max = 4, nknight_max = 4, nsilver_max = 4, + ngold_max = 4, nbishop_max = 2, nrook_max = 2, nking_max = 2 }; + +enum { rank1 = 0, rank2, rank3, rank4, rank5, rank6, rank7, rank8, rank9 }; +enum { file1 = 0, file2, file3, file4, file5, file6, file7, file8, file9 }; + +enum { nhand = 7, nfile = 9, nrank = 9, nsquare = 81 }; + +enum { mask_file1 = (( 1U << 18 | 1U << 9 | 1U ) << 8) }; + +enum { flag_diag1 = b0001, flag_plus = b0010 }; + +enum { score_draw = 1, + score_max_eval = 30000, + score_mate1ply = 32598, + score_inferior = 32599, + score_bound = 32600, + score_foul = 32600 }; + +enum { phase_hash = b0001, + phase_killer1 = b0001 << 1, + phase_killer2 = b0010 << 1, + phase_killer = b0011 << 1, + phase_cap1 = b0001 << 3, + phase_cap_misc = b0010 << 3, + phase_cap = b0011 << 3, + phase_history1 = b0001 << 5, + phase_history2 = b0010 << 5, + phase_history = b0011 << 5, + phase_misc = b0100 << 5 }; + +enum { next_move_hash = 0, next_move_capture, next_move_history2, + next_move_misc }; + +/* next_evasion_hash should be the same as next_move_hash */ +enum { next_evasion_hash = 0, next_evasion_genall, next_evasion_misc }; + +enum { next_quies_gencap, next_quies_captures, next_quies_misc }; + +enum { no_rep = 0, four_fold_rep, perpetual_check, perpetual_check2, + black_superi_rep, white_superi_rep, hash_hit, prev_solution, book_hit, + pv_fail_high, mate_search }; + +enum { record_misc, record_eof, record_next, record_resign, record_drawn, + record_error }; + +enum { black = 0, white = 1 }; + +enum { direc_misc = b0000, + direc_file = b0010, /* | */ + direc_rank = b0011, /* - */ + direc_diag1 = b0100, /* / */ + direc_diag2 = b0101, /* \ */ + flag_cross = b0010, + flag_diag = b0100 }; + +enum { value_null = b0000, + value_upper = b0001, + value_lower = b0010, + value_exact = b0011, + flag_value_up_exact = b0001, + flag_value_low_exact = b0010, + node_do_null = b0100, + node_do_recap = b1000, + node_do_mate = b0001 << 4, + node_mate_threat = b0010 << 4, /* <- don't change it */ + node_do_futile = b0100 << 4, + state_node_end }; +/* note: maximum bits are 8. tlp_state_node uses type unsigned char. */ + +enum { flag_from_ponder = b0001, + flag_refer_rest = b0010 }; + +enum { flag_time = b0001, + flag_history = b0010, + flag_rep = b0100, + flag_detect_hang = b1000, + flag_rejections = b0001 << 4, + flag_nomake_move = b0010 << 4, + flag_nofmargin = b0100 << 4 }; + +/* flags represent status of root move */ +enum { flag_searched = b0001, + flag_failhigh = b0010, + flag_faillow = b0100, + flag_first = b1000 }; + +enum { flag_mated = b0001, + flag_resigned = b0010, + flag_drawn = b0100, + flag_suspend = b1000, + mask_game_end = b1111, + flag_quit = b0001 << 4, + flag_puzzling = b0010 << 4, + flag_pondering = b0100 << 4, + flag_thinking = b1000 << 4, + flag_problem = b0001 << 8, + flag_move_now = b0010 << 8, + flag_quit_ponder = b0100 << 8, + flag_search_error = b0001 << 12, + flag_quiet = b0010 << 12, + flag_reverse = b0100 << 12, + flag_narrow_book = b1000 << 12, + flag_time_extendable = b0001 << 16, + flag_learning = b0010 << 16, + flag_nobeep = b0100 << 16, + flag_nostress = b1000 << 16, + flag_nopeek = b0001 << 20, + flag_noponder = b0010 << 20, + flag_noprompt = b0100 << 20 }; + + +enum { flag_hand_pawn = 1 << 0, + flag_hand_lance = 1 << 5, + flag_hand_knight = 1 << 8, + flag_hand_silver = 1 << 11, + flag_hand_gold = 1 << 14, + flag_hand_bishop = 1 << 17, + flag_hand_rook = 1 << 19 }; + +enum { f_hand_pawn = 0, + e_hand_pawn = 19, + f_hand_lance = 38, + e_hand_lance = 43, + f_hand_knight = 48, + e_hand_knight = 53, + f_hand_silver = 58, + e_hand_silver = 63, + f_hand_gold = 68, + e_hand_gold = 73, + f_hand_bishop = 78, + e_hand_bishop = 81, + f_hand_rook = 84, + e_hand_rook = 87, + fe_hand_end = 90, + f_pawn = 81, + e_pawn = 162, + f_lance = 225, + e_lance = 306, + f_knight = 360, + e_knight = 441, + f_silver = 504, + e_silver = 585, + f_gold = 666, + e_gold = 747, + f_bishop = 828, + e_bishop = 909, + f_horse = 990, + e_horse = 1071, + f_rook = 1152, + e_rook = 1233, + f_dragon = 1314, + e_dragon = 1395, + fe_end = 1476, + + kkp_hand_pawn = 0, + kkp_hand_lance = 19, + kkp_hand_knight = 24, + kkp_hand_silver = 29, + kkp_hand_gold = 34, + kkp_hand_bishop = 39, + kkp_hand_rook = 42, + kkp_hand_end = 45, + kkp_pawn = 36, + kkp_lance = 108, + kkp_knight = 171, + kkp_silver = 252, + kkp_gold = 333, + kkp_bishop = 414, + kkp_horse = 495, + kkp_rook = 576, + kkp_dragon = 657, + kkp_end = 738 }; + +enum { pos_n = fe_end * ( fe_end + 1 ) / 2 }; + +typedef struct { unsigned int p[3]; } bitboard_t; + +typedef struct { bitboard_t gold, silver, knight, lance; } check_table_t; + +#if ! defined(MINIMUM) +typedef struct { fpos_t fpos; unsigned int games, moves, lines; } rpos_t; +typedef struct { + double pawn, lance, knight, silver, gold, bishop, rook; + double pro_pawn, pro_lance, pro_knight, pro_silver, horse, dragon; + float pc_on_sq[nsquare][fe_end*(fe_end+1)/2]; + float kkp[nsquare][nsquare][kkp_end]; +} param_t; +#endif + +typedef enum { mode_write, mode_read_write, mode_read } record_mode_t; + +typedef struct { uint64_t word1, word2; } trans_entry_t; +typedef struct { trans_entry_t prefer, always[2]; } trans_table_t; +typedef struct { int count; unsigned int cnst[2], vec[RAND_N]; }rand_work_t; + +typedef struct { + uint64_t root; + SHARE uint64_t sibling; +} rejections_t; + +typedef struct { + int no1_value, no2_value; + unsigned int no1, no2; +} move_killer_t; + +typedef struct { unsigned int no1, no2; } killer_t; + +typedef struct { + union { char str_move[ MAX_ANSWER ][ 8 ]; } info; + char str_name1[ SIZE_PLAYERNAME ]; + char str_name2[ SIZE_PLAYERNAME ]; + FILE *pf; + unsigned int games, moves, lines; +} record_t; + +typedef struct { + unsigned int a[PLY_MAX]; + unsigned char type; + unsigned char length; + unsigned char depth; +} pv_t; + +typedef struct { + unsigned char ir0, sr0; + unsigned char irl90, srl90; + unsigned char irl45, srl45; + unsigned char irr45, srr45; +} slide_tbl_t; + + +typedef struct { + uint64_t hash_key; + bitboard_t b_occupied, w_occupied; + bitboard_t occupied_rl90, occupied_rl45, occupied_rr45; + bitboard_t b_hdk, w_hdk; + bitboard_t b_tgold, w_tgold; + bitboard_t b_bh, w_bh; + bitboard_t b_rd, w_rd; + bitboard_t b_pawn_attacks, w_pawn_attacks; + bitboard_t b_lance, w_lance; + bitboard_t b_knight, w_knight; + bitboard_t b_silver, w_silver; + bitboard_t b_bishop, w_bishop; + bitboard_t b_rook, w_rook; + bitboard_t b_horse, w_horse; + bitboard_t b_dragon, w_dragon; + bitboard_t b_pawn, w_pawn; + bitboard_t b_gold, w_gold; + bitboard_t b_pro_pawn, w_pro_pawn; + bitboard_t b_pro_lance, w_pro_lance; + bitboard_t b_pro_knight, w_pro_knight; + bitboard_t b_pro_silver, w_pro_silver; + unsigned int hand_black, hand_white; + int material; + signed char asquare[nsquare]; + unsigned char isquare_b_king, isquare_w_king; +} posi_t; + + +typedef struct { + unsigned int hand_black, hand_white; + char turn_to_move; + signed char asquare[nsquare]; +} min_posi_t; + +typedef struct { + uint64_t nodes; + unsigned int move, status; +} root_move_t; + +typedef struct { + unsigned int *move_last; + unsigned int move_cap1; + unsigned int move_cap2; + int phase_done, next_phase, remaining, value_cap1, value_cap2; +} next_move_t; + +/* data: 31 1bit flag_learned */ +/* 30 1bit is_flip */ +/* 15 16bit value */ +typedef struct { + uint64_t key_book; + unsigned int key_responsible, key_probed, key_played; + unsigned int hand_responsible, hand_probed, hand_played; + unsigned int move_played, move_responsible, move_probed, data; +} history_book_learn_t; + +typedef struct tree tree_t; +struct tree { + posi_t posi; + uint64_t rep_board_list[ REP_HIST_LEN ]; + uint64_t node_searched; + unsigned int *move_last[ PLY_MAX ]; + next_move_t anext_move[ PLY_MAX ]; + pv_t pv[ PLY_MAX ]; + move_killer_t amove_killer[ PLY_MAX ]; + unsigned int null_pruning_done; + unsigned int null_pruning_tried; + unsigned int check_extension_done; + unsigned int recap_extension_done; + unsigned int onerp_extension_done; + unsigned int neval_called; + unsigned int nquies_called; + unsigned int nfour_fold_rep; + unsigned int nperpetual_check; + unsigned int nsuperior_rep; + unsigned int nrep_tried; + unsigned int nreject_tried; + unsigned int nreject_done; + unsigned int ntrans_always_hit; + unsigned int ntrans_prefer_hit; + unsigned int ntrans_probe; + unsigned int ntrans_exact; + unsigned int ntrans_lower; + unsigned int ntrans_upper; + unsigned int ntrans_superior_hit; + unsigned int ntrans_inferior_hit; + unsigned int fail_high; + unsigned int fail_high_first; + unsigned int rep_hand_list[ REP_HIST_LEN ]; + unsigned int amove_hash[ PLY_MAX ]; + unsigned int amove[ MOVE_LIST_LEN ]; + unsigned int current_move[ PLY_MAX ]; + killer_t killers[ PLY_MAX ]; + unsigned int hist_nmove[ PLY_MAX ]; + unsigned int hist_move[ PLY_MAX ][ MAX_LEGAL_MOVES ]; + int sort_value[ MAX_LEGAL_MOVES ]; + unsigned short hist_tried[ HIST_SIZE ]; + unsigned short hist_good[ HIST_SIZE ]; + short save_material[ PLY_MAX ]; + short stand_pat[ PLY_MAX+1 ]; + unsigned char nsuc_check[ PLY_MAX+1 ]; +#if defined(TLP) + struct tree *tlp_ptrees_sibling[ TLP_MAX_THREADS ]; + struct tree *tlp_ptree_parent; + lock_t tlp_lock; + volatile int tlp_abort; + volatile int tlp_used; + unsigned short tlp_slot; + short tlp_beta; + short tlp_best; + volatile unsigned char tlp_nsibling; + unsigned char tlp_depth; + unsigned char tlp_state_node; + unsigned char tlp_id; + char tlp_turn; + char tlp_ply; +#endif +}; + + +extern SHARE unsigned int game_status; +extern history_book_learn_t history_book_learn[ HASH_REG_HIST_LEN ]; + +extern int npawn_box; +extern int nlance_box; +extern int nknight_box; +extern int nsilver_box; +extern int ngold_box; +extern int nbishop_box; +extern int nrook_box; + +extern unsigned int ponder_move_list[ MAX_LEGAL_MOVES ]; +extern unsigned int ponder_move; +extern int ponder_nmove; + +extern root_move_t root_move_list[ MAX_LEGAL_MOVES ]; +extern SHARE int root_abort; +extern int root_nrep; +extern int root_nmove; +extern int root_value; +extern int root_alpha; +extern int root_beta; +extern int root_turn; +extern int root_move_cap; +extern int root_nfail_high; +extern int root_nfail_low; +extern int resign_threshold; +extern int n_nobook_move; + +extern uint64_t node_limit; +extern unsigned int node_per_second; +extern unsigned int node_next_signal; +extern unsigned int node_last_check; + +extern unsigned int hash_mask; +extern int trans_table_age; + +extern pv_t last_pv; +extern pv_t last_pv_save; +extern int last_root_value; +extern int last_root_value_save; + +extern SHARE trans_table_t *ptrans_table; +extern trans_table_t *ptrans_table_orig; +extern int log2_ntrans_table; + +extern int depth_limit; + +extern unsigned int time_last_result; +extern unsigned int time_last_eff_search; +extern unsigned int time_last_search; +extern unsigned int time_last_check; +extern unsigned int time_turn_start; +extern unsigned int time_start; +extern unsigned int time_max_limit; +extern unsigned int time_limit; +extern unsigned int time_response; +extern unsigned int sec_limit; +extern unsigned int sec_limit_up; +extern unsigned int sec_limit_depth; +extern unsigned int sec_elapsed; +extern unsigned int sec_b_total; +extern unsigned int sec_w_total; + +extern record_t record_problems; +extern record_t record_game; +extern FILE *pf_book; +extern FILE *pf_hash; +extern int irecord_game; + +extern short p_value[31]; +extern short pc_on_sq[nsquare][fe_end*(fe_end+1)/2]; +extern short kkp[nsquare][nsquare][kkp_end]; + +extern uint64_t ehash_tbl[ EHASH_MASK + 1 ]; +extern unsigned char hash_rejections_parent[ REJEC_MASK+1 ]; +extern rejections_t hash_rejections[ REJEC_MASK+1 ]; +extern rand_work_t rand_work; +extern slide_tbl_t aslide[ nsquare ]; +extern bitboard_t abb_b_knight_attacks[ nsquare ]; +extern bitboard_t abb_b_silver_attacks[ nsquare ]; +extern bitboard_t abb_b_gold_attacks[ nsquare ]; +extern bitboard_t abb_w_knight_attacks[ nsquare ]; +extern bitboard_t abb_w_silver_attacks[ nsquare ]; +extern bitboard_t abb_w_gold_attacks[ nsquare ]; +extern bitboard_t abb_king_attacks[ nsquare ]; +extern bitboard_t abb_obstacle[ nsquare ][ nsquare ]; +extern bitboard_t abb_bishop_attacks_rl45[ nsquare ][ 128 ]; +extern bitboard_t abb_bishop_attacks_rr45[ nsquare ][ 128 ]; +extern bitboard_t abb_file_attacks[ nsquare ][ 128 ]; +extern bitboard_t abb_mask[ nsquare ]; +extern bitboard_t abb_mask_rl90[ nsquare ]; +extern bitboard_t abb_mask_rl45[ nsquare ]; +extern bitboard_t abb_mask_rr45[ nsquare ]; +extern bitboard_t abb_plus_rays[ nsquare ]; +extern bitboard_t abb_minus_rays[ nsquare ]; +extern uint64_t b_pawn_rand[ nsquare ]; +extern uint64_t b_lance_rand[ nsquare ]; +extern uint64_t b_knight_rand[ nsquare ]; +extern uint64_t b_silver_rand[ nsquare ]; +extern uint64_t b_gold_rand[ nsquare ]; +extern uint64_t b_bishop_rand[ nsquare ]; +extern uint64_t b_rook_rand[ nsquare ]; +extern uint64_t b_king_rand[ nsquare ]; +extern uint64_t b_pro_pawn_rand[ nsquare ]; +extern uint64_t b_pro_lance_rand[ nsquare ]; +extern uint64_t b_pro_knight_rand[ nsquare ]; +extern uint64_t b_pro_silver_rand[ nsquare ]; +extern uint64_t b_horse_rand[ nsquare ]; +extern uint64_t b_dragon_rand[ nsquare ]; +extern uint64_t b_hand_pawn_rand[ npawn_max ]; +extern uint64_t b_hand_lance_rand[ nlance_max ]; +extern uint64_t b_hand_knight_rand[ nknight_max ]; +extern uint64_t b_hand_silver_rand[ nsilver_max ]; +extern uint64_t b_hand_gold_rand[ ngold_max ]; +extern uint64_t b_hand_bishop_rand[ nbishop_max ]; +extern uint64_t b_hand_rook_rand[ nrook_max ]; +extern uint64_t w_pawn_rand[ nsquare ]; +extern uint64_t w_lance_rand[ nsquare ]; +extern uint64_t w_knight_rand[ nsquare ]; +extern uint64_t w_silver_rand[ nsquare ]; +extern uint64_t w_gold_rand[ nsquare ]; +extern uint64_t w_bishop_rand[ nsquare ]; +extern uint64_t w_rook_rand[ nsquare ]; +extern uint64_t w_king_rand[ nsquare ]; +extern uint64_t w_pro_pawn_rand[ nsquare ]; +extern uint64_t w_pro_lance_rand[ nsquare ]; +extern uint64_t w_pro_knight_rand[ nsquare ]; +extern uint64_t w_pro_silver_rand[ nsquare ]; +extern uint64_t w_horse_rand[ nsquare ]; +extern uint64_t w_dragon_rand[ nsquare ]; +extern uint64_t w_hand_pawn_rand[ npawn_max ]; +extern uint64_t w_hand_lance_rand[ nlance_max ]; +extern uint64_t w_hand_knight_rand[ nknight_max ]; +extern uint64_t w_hand_silver_rand[ nsilver_max ]; +extern uint64_t w_hand_gold_rand[ ngold_max ]; +extern uint64_t w_hand_bishop_rand[ nbishop_max ]; +extern uint64_t w_hand_rook_rand[ nrook_max ]; +extern unsigned int ai_rook_attacks_r0[ nsquare ][ 128 ]; +extern unsigned int move_evasion_pchk; +extern int p_value_ex[31]; +extern int benefit2promo[15]; +extern int easy_abs; +extern int easy_min; +extern int easy_max; +extern int easy_value; +extern SHARE int fmg_misc; +extern SHARE int fmg_cap; +extern SHARE int fmg_drop; +extern SHARE int fmg_mt; +extern SHARE int fmg_misc_king; +extern SHARE int fmg_cap_king; +extern int iteration_depth; +extern unsigned char book_section[ MAX_SIZE_SECTION+1 ]; +extern unsigned char adirec[nsquare][nsquare]; +extern unsigned char is_same[16][16]; +extern char str_message[ SIZE_MESSAGE ]; +extern char str_cmdline[ SIZE_CMDLINE ]; +extern char str_buffer_cmdline[ SIZE_CMDBUFFER ]; +extern const char *str_error; + +extern const char *astr_table_piece[ 16 ]; +extern const char *str_resign; +extern const char *str_repetition; +extern const char *str_jishogi; +extern const char *str_record_error; +extern const char *str_unexpect_eof; +extern const char *str_ovrflw_line; +extern const char *str_warning; +extern const char *str_on; +extern const char *str_off; +extern const char *str_book; +extern const char *str_hash; +extern const char *str_fv; +extern const char *str_book_error; +extern const char *str_perpet_check; +extern const char *str_bad_cmdline; +extern const char *str_busy_think; +extern const char *str_bad_record; +extern const char *str_bad_board; +extern const char *str_delimiters; +extern const char *str_fmt_line; +extern const char *str_illegal_move; +extern const char *str_double_pawn; +extern const char *str_mate_drppawn; +extern const char *str_fopen_error; +extern const char *str_game_ended; +extern const char *str_io_error; +extern const char *str_spaces; +extern const char *str_king_hang; +#if defined(CSA_LAN) +extern const char *str_server_err; +#endif +extern const char *str_myname; +extern const char *str_version; +extern const min_posi_t min_posi_no_handicap; +extern const short aipos[31]; +extern const char ach_turn[2]; +extern const char ashell_h[ SHELL_H_LEN ]; +extern const unsigned char aifile[ nsquare ]; +extern const unsigned char airank[ nsquare ]; + +void pv_close( tree_t * restrict ptree, int ply, int type ); +void pv_copy( tree_t * restrict ptree, int ply ); +void set_derivative_param( void ); +void set_search_limit_time( int turn ); +void ehash_clear( void ); +void hash_store_pv( const tree_t * restrict ptree, unsigned int move, + int turn ); +void check_futile_score_quies( const tree_t * restrict ptree, + unsigned int move, int old_val, int new_val, + int turn ); +void out_file( FILE *pf, const char *format, ... ); +void out_warning( const char *format, ... ); +void out_error( const char *format, ... ); +void show_prompt( void ); +void make_move_w( tree_t * restrict ptree, unsigned int move, int ply ); +void make_move_b( tree_t * restrict ptree, unsigned int move, int ply ); +void unmake_move_b( tree_t * restrict ptree, unsigned int move, int ply ); +void unmake_move_w( tree_t * restrict ptree, unsigned int move, int ply ); +void ini_rand( unsigned int s ); +void out_CSA( tree_t * restrict ptree, record_t *pr, unsigned int move ); +void out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time ); +void hash_store( const tree_t * restrict ptree, int ply, int depth, int turn, + int value_type, int value, unsigned int move, + unsigned int state_node ); +void *memory_alloc( size_t nbytes ); +void unmake_move_root( tree_t * restrict ptree, unsigned int move ); +void add_rejections_root( tree_t * restrict ptree, unsigned int move_made ); +void sub_rejections_root( tree_t * restrict ptree, unsigned int move_made ); +void add_rejections( tree_t * restrict ptree, int turn, int ply ); +void sub_rejections( tree_t * restrict ptree, int turn, int ply ); +void adjust_time( unsigned int elapsed_new, int turn ); +int popu_count012( unsigned int u0, unsigned int u1, unsigned int u2 ); +int first_one012( unsigned int u0, unsigned int u1, unsigned int u2 ); +int last_one210( unsigned int u2, unsigned int u1, unsigned int u0 ); +int first_one01( unsigned int u0, unsigned int u1 ); +int first_one12( unsigned int u1, unsigned int u2 ); +int last_one01( unsigned int u0, unsigned int u1 ); +int last_one12( unsigned int u1, unsigned int u2 ); +int first_one1( unsigned int u1 ); +int first_one2( unsigned int u2 ); +int last_one0( unsigned int u0 ); +int last_one1( unsigned int u1 ); +int memory_free( void *p ); +int reset_time( unsigned int b_remain, unsigned int w_remain ); +int all_hash_learn_store( void ); +int gen_legal_moves( tree_t * restrict ptree, unsigned int *p0 ); +int rejections_probe( tree_t * restrict ptree, int turn, int ply ); +int ini( tree_t * restrict ptree ); +int fin( void ); +int ponder( tree_t * restrict ptree ); +int hash_learn( const tree_t * restrict ptree, unsigned int move, int value, + int depth ); +int book_on( void ); +int book_off( void ); +int hash_learn_on( void ); +int hash_learn_off( void ); +int is_move_check_b( const tree_t * restrict ptree, unsigned int move ); +int is_move_check_w( const tree_t * restrict ptree, unsigned int move ); +int solve_problems( tree_t * restrict ptree, unsigned int nposition ); +int read_board_rep1( const char *str_line, min_posi_t *pmin_posi ); +int com_turn_start( tree_t * restrict ptree, int flag ); +int read_record( tree_t * restrict ptree, const char *str_file, + unsigned int moves, int flag ); +int out_board( const tree_t * restrict ptree, FILE *pf, unsigned int move, + int flag ); +int make_root_move_list( tree_t * restrict ptree, int flag ); +int record_wind( record_t *pr ); +int book_probe( tree_t * restrict ptree ); +int detect_repetition( tree_t * restrict ptree, int ply, int turn, int nth ); +int is_mate( tree_t * restrict ptree, int ply ); +int is_mate_w_pawn_drop( tree_t * restrict ptree, int sq_drop ); +int is_mate_b_pawn_drop( tree_t * restrict ptree, int sq_drop ); +int clear_trans_table( void ); +int eval_max_score( const tree_t * restrict ptree, unsigned int move, + int stand_pat, int turn, int diff ); +int estimate_score_diff( const tree_t * restrict ptree, unsigned int move, + int turn ); +int eval_material( const tree_t * restrict ptree ); +int ini_trans_table( void ); +int is_hand_eq_supe( unsigned int u, unsigned int uref ); +int is_move_valid( tree_t * restrict ptree, unsigned int move, int turn ); +int is_hash_move_valid( tree_t * restrict ptree, unsigned int move, int turn ); +int iterate( tree_t * restrict ptree, int flag ); +int gen_next_move( tree_t * restrict ptree, int ply, int turn ); +int gen_next_evasion( tree_t * restrict ptree, int ply, int turn ); +int ini_game( tree_t * restrict ptree, const min_posi_t *pmin_posi, int flag, + const char *str_name1, const char *str_name2 ); +int open_history( const char *str_name1, const char *str_name2 ); +int next_cmdline( int is_wait ); +int procedure( tree_t * restrict ptree ); +int get_cputime( unsigned int *ptime ); +int get_elapsed( unsigned int *ptime ); +int interpret_CSA_move( tree_t * restrict ptree, unsigned int *pmove, + const char *str ); +int in_CSA( tree_t * restrict ptree, record_t *pr, unsigned int *pmove, + int do_history ); +int in_CSA_record( FILE * restrict pf, tree_t * restrict ptree ); +int renovate_time( int turn ); +int exam_tree( const tree_t * restrict ptree ); +int rep_check_root( tree_t * restrict ptree ); +int make_move_root( tree_t * restrict ptree, unsigned int move, int flag ); +int search_quies( tree_t * restrict ptree, int alpha, int beta, int turn, + int ply, int qui_ply ); +int search( tree_t * restrict ptree, int alpha, int beta, int turn, + int depth, int ply, unsigned int state_node ); +int searchr( tree_t * restrict ptree, int alpha, int beta, int turn, + int depth ); +int evaluate( tree_t * restrict ptree, int ply, int turn ); +int swap( const tree_t * restrict ptree, unsigned int move, int alpha, + int beta, int turn ); +int file_close( FILE *pf ); +int record_open( record_t *pr, const char *str_file, + record_mode_t record_mode, const char *str_name1, + const char *str_name2 ); +int record_close( record_t *pr ); +unsigned int phash( unsigned int move, int turn ); +unsigned int is_mate_in3ply( tree_t * restrict ptree, int turn, int ply ); +unsigned int is_b_mate_in_1ply( tree_t * restrict ptree ); +unsigned int is_w_mate_in_1ply( tree_t * restrict ptree ); +unsigned int hash_probe( tree_t * restrict ptree, int ply, int depth, int turn, + int alpha, int beta, unsigned int state_node ); +unsigned int rand32( void ); +unsigned int is_black_attacked( const tree_t * restrict ptree, int sq ); +unsigned int is_white_attacked( const tree_t * restrict ptree, int sq ); +unsigned int is_pinned_on_black_king( const tree_t * restrict ptree, + int isquare, int idirec ); +unsigned int is_pinned_on_white_king( const tree_t * restrict ptree, + int isquare, int idirec ); +unsigned int *b_gen_captures( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *b_gen_nocaptures( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *b_gen_drop( tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *b_gen_evasion( tree_t *restrict ptree, + unsigned int * restrict pmove ); +unsigned int *b_gen_checks( tree_t * restrict __ptree__, + unsigned int * restrict pmove ); +unsigned int *b_gen_cap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *b_gen_nocap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_captures( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_nocaptures( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_drop( tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_evasion( tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_checks( tree_t * restrict __ptree__, + unsigned int * restrict pmove ); +unsigned int *w_gen_cap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +unsigned int *w_gen_nocap_nopro_ex2( const tree_t * restrict ptree, + unsigned int * restrict pmove ); +uint64_t hash_func( const tree_t * restrict ptree ); +uint64_t rand64( void ); +trans_entry_t hash_learn_store( const tree_t * restrict ptree, int depth, + int value, unsigned int move ); +FILE *file_open( const char *str_file, const char *str_mode ); +bitboard_t attacks_to_piece( const tree_t * restrict ptree, int sq ); +bitboard_t horse_attacks( const tree_t * restrict ptree, int i ); +const char *str_time( unsigned int time ); +const char *str_time_symple( unsigned int time ); +const char *str_CSA_move( unsigned int move ); +const char *str_CSA_move_plus( tree_t * restrict ptree, unsigned int move, + int ply, int turn ); + +#if defined(MPV) +int root_mpv; +int mpv_num; +int mpv_width; +pv_t mpv_pv[ MPV_MAX_PV*2 + 1 ]; +#endif + +#if defined(TLP) +# define SignKey(word2, word1) word2 ^= ( word1 ) +# define TlpEnd() tlp_end(); +# if ! defined(_WIN32) +extern pthread_attr_t pthread_attr; +# endif +# if defined(MNJ_LAN) +uint64_t tlp_count_node( tree_t * restrict ptree ); +# endif +void tlp_yield( void ); +void tlp_set_abort( tree_t * restrict ptree ); +void lock( lock_t *plock ); +void unlock( lock_t *plock ); +void tlp_end( void ); +int tlp_search( tree_t * restrict ptree, int alpha, int beta, int turn, + int depth, int ply, unsigned int state_node ); +int tlp_split( tree_t * restrict ptree ); +int tlp_start( void ); +int tlp_is_descendant( const tree_t * restrict ptree, int slot_ancestor ); +int lock_init( lock_t *plock ); +int lock_free( lock_t *plock ); +extern lock_t tlp_lock; +extern volatile int tlp_abort; +extern volatile int tlp_idle; +extern volatile int tlp_num; +extern int tlp_max; +extern int tlp_nsplit; +extern int tlp_nabort; +extern int tlp_nslot; +extern SHARE unsigned short tlp_rejections_slot[ REJEC_MASK+1 ]; +extern tree_t tlp_atree_work[ TLP_NUM_WORK ]; +extern tree_t * volatile tlp_ptrees[ TLP_MAX_THREADS ]; +#else /* no TLP */ +# define SignKey(word2, word1) +# define TlpEnd() +extern tree_t tree; +#endif + +#if ! defined(_WIN32) +extern clock_t clk_tck; +#endif + +#if ! defined(NDEBUG) +int exam_bb( const tree_t *ptree ); +#endif + +#if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) ) +# define Out( ... ) out( __VA_ARGS__ ) +void out( const char *format, ... ); +#else +# define Out( ... ) +#endif + +#if ! defined(NO_LOGGING) +extern FILE *pf_log; +extern const char *str_dir_logs; +#endif + +#if defined(NO_STDOUT) || defined(WIN32_PIPE) +# define OutBeep() +# define StdoutStress(x,y) 1 +# define StdoutNormal() 1 +#else +# define OutBeep() out_beep() +# define StdoutStress(x,y) stdout_stress(x,y) +# define StdoutNormal() stdout_normal() +void out_beep( void ); +int stdout_stress( int is_promote, int ifrom ); +int stdout_normal( void ); +#endif + +#if defined(CSA_LAN) && defined(MNJ_LAN) +# define ShutdownClient sckt_shutdown( sckt_mnj ); \ + sckt_shutdown( sckt_csa ); \ + sckt_mnj = sckt_csa = SCKT_NULL +#elif defined(CSA_LAN) +# define ShutdownClient sckt_shutdown( sckt_csa ); \ + sckt_csa = SCKT_NULL +#elif defined(MNJ_LAN) +# define ShutdownClient sckt_shutdown( sckt_mnj ); \ + sckt_mnj = SCKT_NULL +#else +# define ShutdownClient +#endif + +#if defined(CSA_LAN) || defined(MNJ_LAN) +int client_next_game( tree_t * restrict ptree, const char *str_addr, + int iport ); +sckt_t sckt_connect( const char *str_addr, int iport ); +int sckt_shutdown( sckt_t sd ); +int sckt_check( sckt_t sd ); +int sckt_in( sckt_t sd, char *str, int n ); +int sckt_out( sckt_t sd, const char *fmt, ... ); +extern unsigned int time_last_send; +#endif + +#if defined(CSA_LAN) +extern int client_turn; +extern int client_ngame; +extern int client_max_game; +extern long client_port; +extern char client_str_addr[256]; +extern char client_str_id[256]; +extern char client_str_pwd[256]; +extern sckt_t sckt_csa; +#endif + +#if defined(MNJ_LAN) +# define MnjOut( ... ) if ( sckt_mnj != SCKT_NULL ) \ + sckt_out( sckt_mnj, __VA_ARGS__ ) +extern short mnj_tbl[ MNJ_MASK + 1 ]; +extern sckt_t sckt_mnj; +int mnj_reset_tbl( int sd, unsigned int seed ); +int analyze( tree_t * restrict ptree ); +int mnj_posi_id; +unsigned int mnj_move_last; +#else +# define MnjOut( ... ) +#endif + + +#if defined(DEKUNOBOU) || defined(CSA_LAN) || defined(MNJ_LAN) +const char *str_WSAError( const char *str ); +#endif + +#if defined(DEKUNOBOU) && defined(_WIN32) +# define OutDek( ... ) if ( dek_ngame ) { int i = dek_out( __VA_ARGS__ ); \ + if ( i < 0 ) { return i; } } +const char *str_WSAError( const char *str ); +int dek_start( const char *str_sddr, int port_dek, int port_bnz ); +int dek_next_game( tree_t * restrict ptree ); +int dek_in( char *str, int n ); +int dek_out( const char *format, ... ); +int dek_parse( char *str, int len ); +int dek_check( void ); +extern SOCKET dek_socket_in; +extern SOCKET dek_s_accept; +extern u_long dek_ul_addr; +extern unsigned int dek_ngame; +extern unsigned int dek_lost; +extern unsigned int dek_win; +extern int dek_turn; +extern u_short dek_ns; +#else +# define OutDek( ... ) +#endif + +#if defined(CSASHOGI) +# define OutCsaShogi( ... ) out_csashogi( __VA_ARGS__ ) +void out_csashogi( const char *format, ... ); +#else +# define OutCsaShogi( ... ) +#endif + +#define AttackBishop(bb,i) BBOr( bb, AttackDiag1(i), AttackDiag2(i) ) +#define AttackRook(bb,i) (bb) = AttackFile(i); \ + (bb).p[aslide[i].ir0] |= AttackRank(i) + + + +extern check_table_t b_chk_tbl[nsquare]; +extern check_table_t w_chk_tbl[nsquare]; + +#if defined(DBG_EASY) +extern unsigned int easy_move; +#endif + +#if defined(MINIMUM) + +# define MT_CAP_PAWN ( DPawn + DPawn ) +# define MT_CAP_LANCE ( DLance + DLance ) +# define MT_CAP_KNIGHT ( DKnight + DKnight ) +# define MT_CAP_SILVER ( DSilver + DSilver ) +# define MT_CAP_GOLD ( DGold + DGold ) +# define MT_CAP_BISHOP ( DBishop + DBishop ) +# define MT_CAP_ROOK ( DRook + DRook ) +# define MT_CAP_PRO_PAWN ( DProPawn + DPawn ) +# define MT_CAP_PRO_LANCE ( DProLance + DLance ) +# define MT_CAP_PRO_KNIGHT ( DProKnight + DKnight ) +# define MT_CAP_PRO_SILVER ( DProSilver + DSilver ) +# define MT_CAP_HORSE ( DHorse + DBishop ) +# define MT_CAP_DRAGON ( DDragon + DRook ) +# define MT_CAP_KING ( DKing + DKing ) +# define MT_PRO_PAWN ( DProPawn - DPawn ) +# define MT_PRO_LANCE ( DProLance - DLance ) +# define MT_PRO_KNIGHT ( DProKnight - DKnight ) +# define MT_PRO_SILVER ( DProSilver - DSilver ) +# define MT_PRO_BISHOP ( DHorse - DBishop ) +# define MT_PRO_ROOK ( DDragon - DRook ) + +#else + +# define MT_CAP_PAWN ( p_value_ex[ 15 + pawn ] ) +# define MT_CAP_LANCE ( p_value_ex[ 15 + lance ] ) +# define MT_CAP_KNIGHT ( p_value_ex[ 15 + knight ] ) +# define MT_CAP_SILVER ( p_value_ex[ 15 + silver ] ) +# define MT_CAP_GOLD ( p_value_ex[ 15 + gold ] ) +# define MT_CAP_BISHOP ( p_value_ex[ 15 + bishop ] ) +# define MT_CAP_ROOK ( p_value_ex[ 15 + rook ] ) +# define MT_CAP_PRO_PAWN ( p_value_ex[ 15 + pro_pawn ] ) +# define MT_CAP_PRO_LANCE ( p_value_ex[ 15 + pro_lance ] ) +# define MT_CAP_PRO_KNIGHT ( p_value_ex[ 15 + pro_knight ] ) +# define MT_CAP_PRO_SILVER ( p_value_ex[ 15 + pro_silver ] ) +# define MT_CAP_HORSE ( p_value_ex[ 15 + horse ] ) +# define MT_CAP_DRAGON ( p_value_ex[ 15 + dragon ] ) +# define MT_CAP_KING ( DKing + DKing ) +# define MT_PRO_PAWN ( benefit2promo[ 7 + pawn ] ) +# define MT_PRO_LANCE ( benefit2promo[ 7 + lance ] ) +# define MT_PRO_KNIGHT ( benefit2promo[ 7 + knight ] ) +# define MT_PRO_SILVER ( benefit2promo[ 7 + silver ] ) +# define MT_PRO_BISHOP ( benefit2promo[ 7 + bishop ] ) +# define MT_PRO_ROOK ( benefit2promo[ 7 + rook ] ) + +void fill_param_zero( void ); +void ini_param( param_t *p ); +void add_param( param_t *p1, const param_t *p2 ); +void inc_param( const tree_t * restrict ptree, param_t * restrict pd, + double dinc ); +void param_sym( param_t *p ); +void renovate_param( const param_t *pd ); +int learn( tree_t * restrict ptree, int is_ini, int nsteps, + unsigned int max_games, int max_iterations, + int nworker1, int nworker2 ); +int record_setpos( record_t *pr, const rpos_t *prpos ); +int record_getpos( record_t *pr, rpos_t *prpos ); +int record_rewind( record_t *pr ); +int book_create( tree_t * restrict ptree ); +int hash_learn_create( void ); +int out_param( void ); +double calc_penalty( void ); + +#endif /* no MINIMUM */ + +#if ( REP_HIST_LEN - PLY_MAX ) < 1 +# error "REP_HIST_LEN - PLY_MAX is too small." +#endif + +#if defined(CSA_LAN) && '\n' != 0x0a +# error "'\n' is not the ASCII code of LF (0x0a)." +#endif + +#endif /* SHOGI_H */ diff --git a/swap.c b/swap.c new file mode 100644 index 0000000..a195364 --- /dev/null +++ b/swap.c @@ -0,0 +1,391 @@ +#include +#include +#include "shogi.h" + +int +swap( const tree_t * restrict ptree, unsigned int move, int root_alpha, + int root_beta, int turn ) +{ + bitboard_t bb, bb_temp, bb_attack; + unsigned int uattack; + int attacked_piece, from, to, nc, ir0; + int piece_cap, piece, value, xvalue, alpha, beta, is_promo; + + from = (int)I2From( move ); + to = (int)I2To( move ); + + if ( from >= nsquare ) + { + value = 0; + piece = From2Drop( from ); + attacked_piece = p_value_ex[15+piece]; + } + else { + piece = (int)I2PieceMove( move ); + piece_cap = (int)UToCap( move ); + is_promo = (int)I2IsPromote( move ); + + value = p_value_ex[15+piece_cap]; + attacked_piece = p_value_ex[15+piece]; + if ( is_promo ) + { + value += benefit2promo[7+piece]; + attacked_piece += benefit2promo[7+piece]; + } + xvalue = value - attacked_piece - MT_PRO_PAWN; + if ( xvalue >= root_beta ) { return xvalue; } + } + + bb_attack = attacks_to_piece( ptree, to ); + alpha = INT_MIN; + beta = value; + for ( nc = 1;; nc++ ) + { + /* remove an attacker, and add a hidden piece to attack bitmap */ + if ( from < nsquare ) + { + Xor( from, bb_attack ); + switch ( adirec[to][from] ) + { + case direc_rank: + ir0 = aslide[from].ir0; + uattack = AttackRank( from ); + if ( from > to ) { uattack &= abb_plus_rays[from].p[ir0]; } + else { uattack &= abb_minus_rays[from].p[ir0]; } + uattack &= BB_B_RD.p[ir0] | BB_W_RD.p[ir0]; + bb_attack.p[ir0] |= uattack; + break; + + case direc_file: + bb = AttackFile( from ); + BBOr( bb_temp, BB_B_RD, BB_W_RD ); + if ( from > to ) + { + BBOr( bb_temp, bb_temp, BB_BLANCE ); + BBAnd( bb, bb, abb_plus_rays[from] ); + } + else { + BBOr( bb_temp, bb_temp, BB_WLANCE ); + BBAnd( bb, bb, abb_minus_rays[from] ); + } + BBAnd( bb, bb, bb_temp ); + BBOr( bb_attack, bb_attack, bb ); + break; + + case direc_diag1: + bb = AttackDiag1( from ); + if ( from > to ) { BBAnd( bb, bb, abb_plus_rays[from] ); } + else { BBAnd( bb, bb, abb_minus_rays[from] ); } + BBOr( bb_temp, BB_B_BH, BB_W_BH ); + BBAnd( bb, bb, bb_temp ); + BBOr( bb_attack, bb_attack, bb ); + break; + + case direc_diag2: + bb = AttackDiag2( from ); + if ( from > to ) { BBAnd( bb, bb, abb_plus_rays[from] ); } + else { BBAnd( bb, bb, abb_minus_rays[from] ); } + BBOr( bb_temp, BB_B_BH, BB_W_BH ); + BBAnd( bb, bb, bb_temp ); + BBOr( bb_attack, bb_attack, bb ); + break; + + } + } + if ( ! BBToU( bb_attack ) ) { break; } + + /* find a cheapest piece attacking the target */ + turn = Flip( turn ); + value = attacked_piece - value; + if ( turn ) + { + if( BBContract( bb_attack, BB_WPAWN ) ) + { + from = to-nfile; + attacked_piece = MT_CAP_PAWN; + if ( to > 53 ) + { + value += MT_PRO_PAWN; + attacked_piece += MT_PRO_PAWN; + } + goto ab_cut; + } + BBAnd( bb, BB_WLANCE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_LANCE; + if ( to > 53 ) + { + value += MT_PRO_LANCE; + attacked_piece += MT_PRO_LANCE; + } + goto ab_cut; + } + BBAnd( bb, BB_WKNIGHT, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_KNIGHT; + if ( to > 53 ) + { + value += MT_PRO_KNIGHT; + attacked_piece += MT_PRO_KNIGHT; + } + goto ab_cut; + } + BBAnd( bb, BB_WPRO_PAWN, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_PAWN; + goto ab_cut; + } + BBAnd( bb, BB_WPRO_LANCE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_LANCE; + goto ab_cut; + } + BBAnd( bb, BB_WPRO_KNIGHT, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_KNIGHT; + goto ab_cut; + } + BBAnd( bb, BB_WSILVER, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_SILVER; + if ( from > 53 || to > 53 ) + { + value += MT_PRO_SILVER; + attacked_piece += MT_PRO_SILVER; + } + goto ab_cut; + } + BBAnd( bb, BB_WPRO_SILVER, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_SILVER; + goto ab_cut; + } + BBAnd( bb, BB_WGOLD, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_GOLD; + goto ab_cut; + } + BBAnd( bb, BB_WBISHOP, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_BISHOP; + if ( from > 53 || to > 53 ) + { + value += MT_PRO_BISHOP; + attacked_piece += MT_PRO_BISHOP; + } + goto ab_cut; + } + BBAnd( bb, BB_WHORSE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_HORSE; + goto ab_cut; + } + BBAnd( bb, BB_WROOK, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_ROOK; + if ( from > 53 || to > 53 ) + { + value += MT_PRO_ROOK; + attacked_piece += MT_PRO_ROOK; + } + goto ab_cut; + } + BBAnd( bb, BB_WDRAGON, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_DRAGON; + goto ab_cut; + } + if( BBContract( bb_attack, BB_WKING ) ) + { + from = SQ_WKING; + attacked_piece = MT_CAP_KING; + goto ab_cut; + } + } + else { + if( BBContract( bb_attack, BB_BPAWN ) ) + { + from = to+nfile; + attacked_piece = MT_CAP_PAWN; + if ( to < 27 ) + { + value += MT_PRO_PAWN; + attacked_piece += MT_PRO_PAWN; + } + goto ab_cut; + } + BBAnd( bb, BB_BLANCE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_LANCE; + if ( to < 27 ) + { + value += MT_PRO_LANCE; + attacked_piece += MT_PRO_LANCE; + } + goto ab_cut; + } + BBAnd( bb, BB_BKNIGHT, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_KNIGHT; + if ( to < 27 ) + { + value += MT_PRO_KNIGHT; + attacked_piece += MT_PRO_KNIGHT; + } + goto ab_cut; + } + BBAnd( bb, BB_BPRO_PAWN, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_PAWN; + goto ab_cut; + } + BBAnd( bb, BB_BPRO_LANCE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_LANCE; + goto ab_cut; + } + BBAnd( bb, BB_BPRO_KNIGHT, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_KNIGHT; + goto ab_cut; + } + BBAnd( bb, BB_BSILVER, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_SILVER; + if ( from < 27 || to < 27 ) + { + value += MT_PRO_SILVER; + attacked_piece += MT_PRO_SILVER; + } + goto ab_cut; + } + BBAnd( bb, BB_BPRO_SILVER, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_PRO_SILVER; + goto ab_cut; + } + BBAnd( bb, BB_BGOLD, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_GOLD; + goto ab_cut; + } + BBAnd( bb, BB_BBISHOP, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_BISHOP; + if ( from < 27 || to < 27 ) + { + value += MT_PRO_BISHOP; + attacked_piece += MT_PRO_BISHOP; + } + goto ab_cut; + } + BBAnd( bb, BB_BHORSE, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_HORSE; + goto ab_cut; + } + BBAnd( bb, BB_BROOK, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_ROOK; + if ( from < 27 || to < 27 ) + { + value += MT_PRO_ROOK; + attacked_piece += MT_PRO_ROOK; + } + goto ab_cut; + } + BBAnd( bb, BB_BDRAGON, bb_attack ); + if( BBToU( bb ) ) + { + from = FirstOne( bb ); + attacked_piece = MT_CAP_DRAGON; + goto ab_cut; + } + if( BBContract( bb_attack, BB_BKING ) ) + { + from = SQ_BKING; + attacked_piece = MT_CAP_KING; + goto ab_cut; + } + } + break; + + ab_cut: + if ( nc & 1 ) + { + if ( -value > alpha ) + { + if ( -value >= beta ) { return beta; } + if ( -value >= root_beta ) { return -value; } + alpha = -value; + } + else { + xvalue = attacked_piece + MT_PRO_PAWN - value; + if ( xvalue <= alpha ) { return alpha; } + if ( xvalue <= root_alpha ) { return xvalue; } + } + } + else { + if ( value < beta ) + { + if ( value <= alpha ) { return alpha; } + if ( value <= root_alpha ) { return value; } + beta = value; + } + else { + xvalue = value - attacked_piece - MT_PRO_PAWN; + if ( xvalue >= beta ) { return beta; } + if ( xvalue >= root_beta ) { return xvalue; } + } + } + } + + if ( nc & 1 ) { return beta; } + else { return alpha; } +} diff --git a/thread.c b/thread.c new file mode 100644 index 0000000..c88461a --- /dev/null +++ b/thread.c @@ -0,0 +1,458 @@ +#include +#include +#include +#if defined(_WIN32) +# include +#else +# include +#endif +#include "shogi.h" + +#if defined(TLP) + +# if defined(_WIN32) +static unsigned int __stdcall start_address( void *arg ); +# else +static void *start_address( void *arg ); +# endif + +static tree_t *find_child( void ); +static void init_state( const tree_t * restrict parent, + tree_t * restrict child ); +static void copy_state( tree_t * restrict parent, + const tree_t * restrict child, int value ); +static void wait_work( int tid, tree_t *parent ); + +int +tlp_start( void ) +{ + int work[ TLP_MAX_THREADS ]; + int num; + + if ( tlp_num ) { return 1; } + + for ( num = 1; num < tlp_max; num++ ) + { + work[num] = num; + +# if defined(_WIN32) + if ( ! _beginthreadex( 0, 0, start_address, work+num, 0, 0 ) ) + { + str_error = "_beginthreadex() failed."; + return -2; + } +# else + { + pthread_t pt; + if ( pthread_create( &pt, &pthread_attr, start_address, work+num ) ) + { + str_error = "pthread_create() failed."; + return -2; + } + } +# endif + } + while ( tlp_num +1 < tlp_max ) { tlp_yield(); } + + return 1; +} + + +void +tlp_end( void ) +{ + tlp_abort = 1; + while ( tlp_num ) { tlp_yield(); } + tlp_abort = 0; +} + + +int +tlp_split( tree_t * restrict ptree ) +{ + tree_t *child; + int num, nchild, i; + + lock( &tlp_lock ); + + if ( ! tlp_idle || ptree->tlp_abort ) + { + unlock( &tlp_lock ); + return 0; + } + + tlp_ptrees[ ptree->tlp_id ] = NULL; + ptree->tlp_nsibling = 0; + nchild = 0; + for ( num = 0; num < tlp_max; num++ ) + { + if ( tlp_ptrees[num] ) { ptree->tlp_ptrees_sibling[num] = 0; } + else { + child = find_child(); + if ( ! child ) { continue; } + + nchild += 1; + + for ( i=0; itlp_ptrees_sibling[i] = NULL; } + child->tlp_ptree_parent = ptree; + child->tlp_id = (unsigned char)num; + child->tlp_used = 1; + child->tlp_abort = 0; + ptree->tlp_ptrees_sibling[num] = child; + ptree->tlp_nsibling += 1; + init_state( ptree, child ); + + tlp_ptrees[num] = child; + } + } + + if ( ! nchild ) + { + tlp_ptrees[ ptree->tlp_id ] = ptree; + unlock( &tlp_lock ); + return 0; + } + + tlp_nsplit += 1; + tlp_idle += 1; + + unlock( &tlp_lock ); + + wait_work( ptree->tlp_id, ptree ); + + return 1; +} + + +void +tlp_set_abort( tree_t * restrict ptree ) +{ + int num; + + ptree->tlp_abort = 1; + for ( num = 0; num < tlp_max; num++ ) + if ( ptree->tlp_ptrees_sibling[num] ) + { + tlp_set_abort( ptree->tlp_ptrees_sibling[num] ); + } +} + + +#if defined(MNJ_LAN) +uint64_t +tlp_count_node( tree_t * restrict ptree ) +{ + uint64_t uret = ptree->node_searched; + int num; + + for ( num = 0; num < tlp_max; num++ ) + if ( ptree->tlp_ptrees_sibling[num] ) + { + uret += tlp_count_node( ptree->tlp_ptrees_sibling[num] ); + } + + return uret; +} +#endif + + +int +tlp_is_descendant( const tree_t * restrict ptree, int slot_ancestor ) +{ + int slot = (int)ptree->tlp_slot; + + for ( ;; ) { + if ( slot == slot_ancestor ) { return 1; } + else if ( ! slot ) { return 0; } + else { slot = tlp_atree_work[slot].tlp_ptree_parent->tlp_slot; } + } +} + + +int +lock_init( lock_t *plock ) +{ +# if defined(_MSC_VER) + *plock = 0; +# elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + *plock = 0; +# else + if ( pthread_mutex_init( plock, 0 ) ) + { + str_error = "pthread_mutex_init() failed."; + return -1; + } +# endif + return 1; +} + + +int +lock_free( lock_t *plock ) +{ +# if defined(_MSC_VER) + *plock = 0; +# elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + *plock = 0; +# else + if ( pthread_mutex_destroy( plock ) ) + { + str_error = "pthread_mutex_destroy() failed."; + return -1; + } +# endif + return 1; +} + + +void +unlock( lock_t *plock ) +{ +# if defined(_MSC_VER) + *plock = 0; +# elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + *plock = 0; +# else + pthread_mutex_unlock( plock ); +# endif +} + + +void +lock( lock_t *plock ) +{ +# if defined(_MSC_VER) + long l; + + for ( ;; ) + { + l = _InterlockedExchange( (void *)plock, 1 ); + if ( ! l ) { return; } + while ( *plock ); + } +# elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + int itemp; + + for ( ;; ) + { + asm ( "1: movl $1, %1 \n\t" + " xchgl (%0), %1 \n\t" + : "=g" (plock), "=r" (itemp) : "0" (plock) ); + if ( ! itemp ) { return; } + while ( *plock ); + } +# else + pthread_mutex_lock( plock ); +# endif +} + + +void +tlp_yield( void ) +{ +#if defined(_WIN32) + Sleep( 0 ); +#else + sched_yield(); +#endif +} + + +# if defined(_MSC_VER) +static unsigned int __stdcall start_address( void *arg ) +# else +static void *start_address( void *arg ) +#endif +{ + int tid = *(int *)arg; + + tlp_ptrees[tid] = NULL; + + lock( &tlp_lock ); + Out( "Hi from thread no.%d\n", tid ); + tlp_num += 1; + tlp_idle += 1; + unlock( &tlp_lock ); + + wait_work( tid, NULL ); + + lock( &tlp_lock ); + Out( "Bye from thread no.%d\n", tid ); + tlp_num -= 1; + tlp_idle -= 1; + unlock( &tlp_lock ); + + return 0; +} + + +static void +wait_work( int tid, tree_t *parent ) +{ + tree_t *slot; + int value; + + for ( ;; ) { + + for ( ;; ) { + if ( tlp_ptrees[tid] ) { break; } + if ( parent && ! parent->tlp_nsibling ) { break; } + if ( tlp_abort ) { return; } + + tlp_yield(); + } + + lock( &tlp_lock ); + if ( ! tlp_ptrees[tid] ) { tlp_ptrees[tid] = parent; } + tlp_idle -= 1; + unlock( &tlp_lock ); + + slot = tlp_ptrees[tid]; + if ( slot == parent ) { return; } + + value = tlp_search( slot, + slot->tlp_ptree_parent->tlp_best, + slot->tlp_ptree_parent->tlp_beta, + slot->tlp_ptree_parent->tlp_turn, + slot->tlp_ptree_parent->tlp_depth, + slot->tlp_ptree_parent->tlp_ply, + slot->tlp_ptree_parent->tlp_state_node ); + + lock( &tlp_lock ); + copy_state( slot->tlp_ptree_parent, slot, value ); + slot->tlp_ptree_parent->tlp_nsibling -= 1; + slot->tlp_ptree_parent->tlp_ptrees_sibling[tid] = NULL; + slot->tlp_used = 0; + tlp_ptrees[tid] = NULL; + tlp_idle += 1; + unlock( &tlp_lock); + } +} + + +static tree_t * +find_child( void ) +{ + int i; + + for ( i = 1; i < TLP_NUM_WORK && tlp_atree_work[i].tlp_used; i++ ); + if ( i == TLP_NUM_WORK ) { return NULL; } + if ( i > tlp_nslot ) { tlp_nslot = i; } + + return tlp_atree_work + i; +} + + +static void +init_state( const tree_t * restrict parent, tree_t * restrict child ) +{ + int i, ply; + + child->posi = parent->posi; + child->node_searched = 0; + child->null_pruning_done = 0; + child->null_pruning_tried = 0; + child->check_extension_done = 0; + child->recap_extension_done = 0; + child->onerp_extension_done = 0; + child->neval_called = 0; + child->nquies_called = 0; + child->nfour_fold_rep = 0; + child->nperpetual_check = 0; + child->nsuperior_rep = 0; + child->nrep_tried = 0; + child->nreject_tried = 0; + child->nreject_done = 0; + child->ntrans_always_hit = 0; + child->ntrans_prefer_hit = 0; + child->ntrans_probe = 0; + child->ntrans_exact = 0; + child->ntrans_lower = 0; + child->ntrans_upper = 0; + child->ntrans_superior_hit = 0; + child->ntrans_inferior_hit = 0; + child->fail_high = 0; + child->fail_high_first = 0; + ply = parent->tlp_ply; + + child->anext_move[ply].value_cap1 = parent->anext_move[ply].value_cap1; + child->anext_move[ply].value_cap2 = parent->anext_move[ply].value_cap2; + child->anext_move[ply].move_cap1 = parent->anext_move[ply].move_cap1; + child->anext_move[ply].move_cap2 = parent->anext_move[ply].move_cap2; + + child->move_last[ply] = child->amove; + child->stand_pat[ply] = parent->stand_pat[ply]; + child->current_move[ply-1] = parent->current_move[ply-1]; + child->nsuc_check[ply-1] = parent->nsuc_check[ply-1]; + child->nsuc_check[ply] = parent->nsuc_check[ply]; + + memcpy( child->hist_good, parent->hist_good, sizeof(parent->hist_good) ); + memcpy( child->hist_tried, parent->hist_tried, sizeof(parent->hist_tried) ); + + for ( i = 0; i < root_nrep + ply - 1; i++ ) + { + child->rep_board_list[i] = parent->rep_board_list[i]; + child->rep_hand_list[i] = parent->rep_hand_list[i]; + } + for ( i = ply; i < PLY_MAX; i++ ) + { + child->amove_killer[i] = parent->amove_killer[i]; + child->killers[i] = parent->killers[i]; + } + +} + + +static void +copy_state( tree_t * restrict parent, const tree_t * restrict child, + int value ) +{ + int i, ply; + + parent->check_extension_done += child->check_extension_done; + parent->recap_extension_done += child->recap_extension_done; + + if ( ! child->node_searched ) { return; } + + parent->node_searched += child->node_searched; + parent->null_pruning_done += child->null_pruning_done; + parent->null_pruning_tried += child->null_pruning_tried; + parent->onerp_extension_done += child->onerp_extension_done; + parent->neval_called += child->neval_called; + parent->nquies_called += child->nquies_called; + parent->nrep_tried += child->nrep_tried; + parent->nfour_fold_rep += child->nfour_fold_rep; + parent->nperpetual_check += child->nperpetual_check; + parent->nsuperior_rep += child->nsuperior_rep; + parent->nreject_tried += child->nreject_tried; + parent->nreject_done += child->nreject_done; + parent->ntrans_always_hit += child->ntrans_always_hit; + parent->ntrans_prefer_hit += child->ntrans_prefer_hit; + parent->ntrans_probe += child->ntrans_probe; + parent->ntrans_exact += child->ntrans_exact; + parent->ntrans_lower += child->ntrans_lower; + parent->ntrans_upper += child->ntrans_upper; + parent->ntrans_superior_hit += child->ntrans_superior_hit; + parent->ntrans_inferior_hit += child->ntrans_inferior_hit; + parent->fail_high_first += child->fail_high_first; + parent->fail_high += child->fail_high; + + if ( child->tlp_abort || value <= parent->tlp_best ) { return; } + + ply = parent->tlp_ply; + parent->tlp_best = (short)value; + parent->pv[ply] = child->pv[ply]; + parent->current_move[ply] = child->current_move[ply]; + + memcpy( parent->hist_tried, child->hist_tried, sizeof(child->hist_tried) ); + memcpy( parent->hist_good, child->hist_good, sizeof(child->hist_good) ); + + for ( i = ply; i < PLY_MAX; i++ ) + { + parent->amove_killer[i] = child->amove_killer[i]; + parent->killers[i] = child->killers[i]; + } +} + + +#endif /* TLP */ diff --git a/time.c b/time.c new file mode 100644 index 0000000..203c3bf --- /dev/null +++ b/time.c @@ -0,0 +1,300 @@ +#include +#include +#if ! defined(_WIN32) +# include +# include +#endif +#include "shogi.h" + + +void +set_search_limit_time( int turn ) +/* + [inputs] + global variables: + sec_limit time limit of a side for an entire game in seconds + sec_limit_up time limit for a move when the ordinary time has run out. + the time is known as 'byo-yomi'. + sec_b_total seconds spent by black + sec_w_total seconds spent by white + time_response + ( game_status & flag_puzzling ) time-control for puzzling-search + ( game_status & flag_pondering ) time-control for pondering-search + ( game_status & flag_time_extendable ) bonanza isn't punctual to the time + + argument: + turn a side to move + + [outputs] + global variables: + time_limit tentative deadline for searching in millisecond + time_max_limit maximum allowed time to search in millisecond + + [working area] + local variables: + sec_elapsed second spent by the side to move + sec_left time left for the side in seconds + u0 tentative deadline for searching in second + u1 maximum allowed time to search in second +*/ +{ + unsigned int u0, u1; + + /* no time-control */ + if ( sec_limit_up == UINT_MAX || ( game_status & flag_pondering ) ) + { + time_max_limit = time_limit = UINT_MAX; + return; + } + + /* not punctual to the time */ + if ( ! sec_limit && ( game_status & flag_time_extendable ) ) + { + u0 = sec_limit_up; + u1 = sec_limit_up * 5U; + } + /* have byo-yomi */ + else if ( sec_limit_up ) + { + unsigned int umax, umin, sec_elapsed, sec_left; + + sec_elapsed = turn ? sec_w_total : sec_b_total; + sec_left = ( sec_elapsed <= sec_limit ) ? sec_limit - sec_elapsed : 0; + u0 = ( sec_left + ( TC_NMOVE / 2U ) ) / TC_NMOVE; + + /* + t = 2s is not beneficial since 2.8s are almost the same as 1.8s. + So that, we rather want to use up the ordinary time. + */ + if ( u0 == 2U ) { u0 = 3U; } + + /* 'byo-yomi' is so long that the ordinary time-limit is negligible. */ + if ( u0 < sec_limit_up * 3U / 2U ) { u0 = sec_limit_up * 3U / 2U; } + + u1 = u0 * 5U; + + umax = sec_left + sec_limit_up; + umin = 1; + + if ( umax < u0 ) { u0 = umax; } + if ( umax < u1 ) { u1 = umax; } + if ( umin > u0 ) { u0 = umin; } + if ( umin > u1 ) { u1 = umin; } + } + /* no byo-yomi */ + else { + unsigned int sec_elapsed, sec_left; + + sec_elapsed = turn ? sec_w_total : sec_b_total; + + /* We have some seconds left to think. */ + if ( sec_elapsed + SEC_MARGIN < sec_limit ) + { + sec_left = sec_limit - sec_elapsed; + u0 = ( sec_left + ( TC_NMOVE / 2U ) ) / TC_NMOVE; + + /* t = 2s is not beneficial since 2.8s is almost the same as 1.8s. */ + /* So that, we rather want to save the time. */ + if ( u0 == 2U ) { u0 = 1U; } + u1 = u0 * 5U; + } + /* We are running out of time... */ + else { u0 = u1 = 1U; } + } + + time_limit = u0 * 1000U + 1000U - time_response; + time_max_limit = u1 * 1000U + 1000U - time_response; + + if ( game_status & flag_puzzling ) + { + time_max_limit = time_max_limit / 5U; + time_limit = time_max_limit / 5U; + } + + Out( "- time ctrl: %u -- %u\n", time_limit, time_max_limit ); +} + + +int +renovate_time( int turn ) +{ + unsigned int te, time_elapsed; + int iret; + + iret = get_elapsed( &te ); + if ( iret < 0 ) { return iret; } + + time_elapsed = te - time_turn_start; + sec_elapsed = time_elapsed / 1000U; + if ( ! sec_elapsed ) { sec_elapsed = 1U; } + + if ( turn ) { sec_w_total += sec_elapsed; } + else { sec_b_total += sec_elapsed; } + + time_turn_start = te; + + return 1; +} + + +void +adjust_time( unsigned int elapsed_new, int turn ) +{ + const char *str = "TIME SKEW DETECTED"; + + if ( turn ) + { + if ( sec_w_total + elapsed_new < sec_elapsed ) + { + out_warning( str ); + sec_w_total = 0; + } + else { sec_w_total = sec_w_total + elapsed_new - sec_elapsed; }; + } + else { + if ( sec_b_total + elapsed_new < sec_elapsed ) + { + out_warning( str ); + sec_b_total = 0; + } + else { sec_b_total = sec_b_total + elapsed_new - sec_elapsed; }; + } +} + + +int +reset_time( unsigned int b_remain, unsigned int w_remain ) +{ + if ( sec_limit_up == UINT_MAX ) + { + str_error = "no time limit is set"; + return -2; + } + + if ( b_remain > sec_limit || w_remain > sec_limit ) + { + snprintf( str_message, SIZE_MESSAGE, + "time remaining can't be larger than %u", sec_limit ); + str_error = str_message; + return -2; + } + + sec_b_total = sec_limit - b_remain; + sec_w_total = sec_limit - w_remain; + Out( " elapsed: b%u, w%u\n", sec_b_total, sec_w_total ); + + return 1; +} + + +const char * +str_time( unsigned int time ) +{ + static char str[32]; + unsigned int time_min = time / 60000; + unsigned int time_sec = time / 1000; + unsigned int time_mil = time % 1000; + + if ( time_min < 60 ) + { + snprintf( str, 32, "%02u:%02u.%02u", + time_min, time_sec%60, time_mil/10 ); + } + else { + snprintf( str, 32, "%02u:%02u:%02u.%02u", + time_min / 60, time_min % 60, time_sec%60, time_mil/10 ); + } + return str; +} + + +const char * +str_time_symple( unsigned int time ) +{ + static char str[32]; + unsigned int time_min = time / 60000; + unsigned int time_sec = time / 1000; + unsigned int time_mil = time % 1000; + + if ( time_min == 0 ) + { + snprintf( str, 32, "%5.2f", (float)(time_sec*1000+time_mil)/1000.0 ); + } + else { snprintf( str, 32, "%2u:%02u", time_min, time_sec % 60 ); } + + return str; +} + + +int +get_cputime( unsigned int *ptime ) +{ +#if defined(_WIN32) + HANDLE hProcess; + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + ULARGE_INTEGER uli_temp; + + hProcess = GetCurrentProcess(); + if ( GetProcessTimes( hProcess, &CreationTime, &ExitTime, + &KernelTime, &UserTime ) ) + { + uli_temp.LowPart = UserTime.dwLowDateTime; + uli_temp.HighPart = UserTime.dwHighDateTime; + *ptime = (unsigned int)( uli_temp.QuadPart / 10000 ); + } + else { +# if 0 /* winnt */ + str_error = "GetProcessTimes() failed."; + return -1; +# else /* win95 */ + *ptime = 0U; +# endif + } +#else + clock_t clock_temp; + struct tms t; + + if ( times( &t ) == -1 ) + { + str_error = "times() faild."; + return -1; + } + clock_temp = t.tms_utime + t.tms_stime + t.tms_cutime + t.tms_cstime; + *ptime = (unsigned int)( clock_temp / clk_tck ) * 1000; + clock_temp %= clk_tck; + *ptime += (unsigned int)( (clock_temp * 1000) / clk_tck ); +#endif /* no _WIN32 */ + + return 1; +} + + +int +get_elapsed( unsigned int *ptime ) +{ +#if defined(_WIN32) + /* + try timeBeginPeriod() and timeGetTime(), + or QueryPerformanceFrequency and QueryPerformanceCounter() + */ + FILETIME FileTime; + ULARGE_INTEGER uli_temp; + + GetSystemTimeAsFileTime( &FileTime ); + uli_temp.LowPart = FileTime.dwLowDateTime; + uli_temp.HighPart = FileTime.dwHighDateTime; + *ptime = (unsigned int)( uli_temp.QuadPart / 10000U ); +#else + struct timeval timeval; + + if ( gettimeofday( &timeval, NULL ) == -1 ) + { + str_error = "gettimeofday() faild."; + return -1; + } + *ptime = (unsigned int)timeval.tv_sec * 1000; + *ptime += (unsigned int)timeval.tv_usec / 1000; +#endif /* no _WIN32 */ + + return 1; +} diff --git a/unmake.c b/unmake.c new file mode 100644 index 0000000..91e363d --- /dev/null +++ b/unmake.c @@ -0,0 +1,326 @@ +#include +#include "shogi.h" + + +#define CapW( PIECE, piece, pro_piece ) Xor( to, BB_W ## PIECE ); \ + HAND_B -= flag_hand_ ## piece; \ + BOARD[to] = -pro_piece + +#define CapB( PIECE, piece, pro_piece ) Xor( to, BB_B ## PIECE ); \ + HAND_W -= flag_hand_ ## piece; \ + BOARD[to] = pro_piece + +#define NocapNopro( PIECE, piece ) SetClear( BB_ ## PIECE ); \ + BOARD[from] = piece + +#define NocapPro( PIECE , PRO_PIECE, piece ) Xor( from, BB_ ## PIECE ); \ + Xor( to, BB_ ## PRO_PIECE ); \ + BOARD[from] = piece + + +void +unmake_move_b( tree_t * restrict ptree, unsigned int move, int ply ) +{ + int from = (int)I2From(move); + int to = (int)I2To(move); + int nrep = root_nrep + ply - 1; + + HASH_KEY = ptree->rep_board_list[nrep]; + MATERIAL = ptree->save_material[ply]; + + if ( from >= nsquare ) + { + switch( From2Drop(from) ) + { + case pawn: Xor( to, BB_BPAWN ); + Xor( to-nfile, BB_BPAWN_ATK ); + HAND_B += flag_hand_pawn; break; + case lance: Xor( to, BB_BLANCE ); + HAND_B += flag_hand_lance; break; + case knight: Xor( to, BB_BKNIGHT ); + HAND_B += flag_hand_knight; break; + case silver: Xor( to, BB_BSILVER ); + HAND_B += flag_hand_silver; break; + case gold: Xor( to, BB_BGOLD ); + Xor( to, BB_BTGOLD ); + HAND_B += flag_hand_gold; break; + case bishop: Xor( to, BB_BBISHOP ); + Xor( to, BB_B_BH ); + HAND_B += flag_hand_bishop; break; + default: assert( From2Drop(from) == rook ); + Xor( to, BB_BROOK ); + Xor( to, BB_B_RD ); + HAND_B += flag_hand_rook; break; + } + + BOARD[to] = empty; + Xor( to, BB_BOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + else { + const int ipiece_move = (int)I2PieceMove(move); + const int ipiece_cap = (int)UToCap(move); + const int is_promote = (int)I2IsPromote(move); + bitboard_t bb_set_clear; + + BBOr( bb_set_clear, abb_mask[from], abb_mask[to] ); + SetClear( BB_BOCCUPY ); + + if ( is_promote ) switch( ipiece_move ) + { + case pawn: NocapPro( BPAWN, BPRO_PAWN, pawn ); + Xor( to, BB_BPAWN_ATK ); + Xor( to, BB_BTGOLD ); break; + case lance: NocapPro( BLANCE, BPRO_LANCE, lance ); + Xor( to, BB_BTGOLD ); break; + case knight: NocapPro( BKNIGHT, BPRO_KNIGHT, knight ); + Xor( to, BB_BTGOLD ); break; + case silver: NocapPro( BSILVER, BPRO_SILVER, silver ); + Xor( to, BB_BTGOLD ); break; + case bishop: NocapPro( BBISHOP, BHORSE, bishop ); + Xor( to, BB_B_HDK ); + SetClear( BB_B_BH ); break; + default: assert( ipiece_move == rook ); + NocapPro( BROOK, BDRAGON, rook ); + Xor( to, BB_B_HDK ); + SetClear( BB_B_RD ); break; + } + else switch ( ipiece_move ) + { + case pawn: NocapNopro( BPAWN, pawn ); + Xor( to-nfile, BB_BPAWN_ATK ); + Xor( to, BB_BPAWN_ATK ); break; + case lance: NocapNopro( BLANCE, lance ); break; + case knight: NocapNopro( BKNIGHT, knight ); break; + case silver: NocapNopro( BSILVER, silver ); break; + case gold: NocapNopro( BGOLD, gold ); + SetClear( BB_BTGOLD ); break; + case bishop: NocapNopro( BBISHOP, bishop ); + SetClear( BB_B_BH ); break; + case rook: NocapNopro( BROOK, rook); + SetClear( BB_B_RD ); break; + case king: BOARD[from] = king; + SQ_BKING = (char)from; + SetClear( BB_B_HDK ); break; + case pro_pawn: NocapNopro( BPRO_PAWN, pro_pawn ); + SetClear( BB_BTGOLD ); break; + case pro_lance: NocapNopro( BPRO_LANCE, pro_lance ); + SetClear( BB_BTGOLD ); break; + case pro_knight: NocapNopro( BPRO_KNIGHT, pro_knight ); + SetClear( BB_BTGOLD ); break; + case pro_silver: NocapNopro( BPRO_SILVER, pro_silver ); + SetClear( BB_BTGOLD ); break; + case horse: NocapNopro( BHORSE, horse ); + SetClear( BB_B_HDK ); + SetClear( BB_B_BH ); break; + default: assert( ipiece_move == dragon ); + NocapNopro( BDRAGON, dragon ); + SetClear( BB_B_HDK ); + SetClear( BB_B_RD ); break; + } + + if ( ipiece_cap ) + { + switch( ipiece_cap ) + { + case pawn: CapW( PAWN, pawn, pawn ); + Xor( to+nfile, BB_WPAWN_ATK ); break; + case lance: CapW( LANCE, lance, lance); break; + case knight: CapW( KNIGHT, knight, knight); break; + case silver: CapW( SILVER, silver, silver); break; + case gold: CapW( GOLD, gold, gold); + Xor( to, BB_WTGOLD ); break; + case bishop: CapW( BISHOP, bishop, bishop); + Xor( to, BB_W_BH ); break; + case rook: CapW( ROOK, rook, rook); + Xor( to, BB_W_RD ); break; + case pro_pawn: CapW( PRO_PAWN, pawn, pro_pawn); + Xor( to, BB_WTGOLD ); break; + case pro_lance: CapW( PRO_LANCE, lance, pro_lance); + Xor( to, BB_WTGOLD ); break; + case pro_knight: CapW( PRO_KNIGHT, knight, pro_knight); + Xor( to, BB_WTGOLD ); break; + case pro_silver: CapW(PRO_SILVER, silver, pro_silver); + Xor( to, BB_WTGOLD ); break; + case horse: CapW(HORSE, bishop, horse); + Xor( to, BB_W_HDK ); + Xor( to, BB_W_BH ); break; + default: assert( ipiece_cap == dragon ); + CapW(DRAGON, rook, dragon); + Xor( to, BB_W_HDK ); + Xor( to, BB_W_RD ); break; + } + Xor( to, BB_WOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag1( from, OCCUPIED_DIAG1 ); + XorDiag2( from, OCCUPIED_DIAG2 ); + } + else { + BOARD[to] = empty; + SetClearFile( from, to, OCCUPIED_FILE ); + SetClearDiag1( from, to, OCCUPIED_DIAG1 ); + SetClearDiag2( from, to, OCCUPIED_DIAG2 ); + } + } + + assert( exam_bb( ptree ) ); +} + + +void +unmake_move_w( tree_t * restrict ptree, unsigned int move, int ply ) +{ + int from = (int)I2From(move); + int to = (int)I2To(move); + int nrep = root_nrep + ply - 1; + + HASH_KEY = ptree->rep_board_list[nrep]; + MATERIAL = ptree->save_material[ply]; + + if ( from >= nsquare ) + { + switch( From2Drop(from) ) + { + case pawn: Xor( to, BB_WPAWN ); + Xor( to+nfile, BB_WPAWN_ATK ); + HAND_W += flag_hand_pawn; break; + case lance: Xor( to, BB_WLANCE ); + HAND_W += flag_hand_lance; break; + case knight: Xor( to, BB_WKNIGHT ); + HAND_W += flag_hand_knight; break; + case silver: Xor( to, BB_WSILVER ); + HAND_W += flag_hand_silver; break; + case gold: Xor( to, BB_WGOLD ); + Xor( to, BB_WTGOLD ); + HAND_W += flag_hand_gold; break; + case bishop: Xor( to, BB_WBISHOP ); + Xor( to, BB_W_BH ); + HAND_W += flag_hand_bishop; break; + default: assert( From2Drop(from) == rook ); + Xor( to, BB_WROOK ); + Xor( to, BB_W_RD ); + HAND_W += flag_hand_rook; break; + } + + BOARD[to] = empty; + Xor( to, BB_WOCCUPY ); + XorFile( to, OCCUPIED_FILE ); + XorDiag2( to, OCCUPIED_DIAG2 ); + XorDiag1( to, OCCUPIED_DIAG1 ); + } + else { + const int ipiece_move = (int)I2PieceMove(move); + const int ipiece_cap = (int)UToCap(move); + const int is_promote = (int)I2IsPromote(move); + bitboard_t bb_set_clear; + + BBOr( bb_set_clear, abb_mask[from], abb_mask[to] ); + SetClear( BB_WOCCUPY ); + + if ( is_promote ) switch( ipiece_move ) + { + case pawn: NocapPro( WPAWN, WPRO_PAWN, -pawn ); + Xor( to, BB_WPAWN_ATK ); + Xor( to, BB_WTGOLD ); break; + case lance: NocapPro( WLANCE, WPRO_LANCE, -lance ); + Xor( to, BB_WTGOLD ); break; + case knight: NocapPro( WKNIGHT, WPRO_KNIGHT, -knight ); + Xor( to, BB_WTGOLD ); break; + case silver: NocapPro( WSILVER, WPRO_SILVER, -silver ); + Xor( to, BB_WTGOLD ); break; + case bishop: NocapPro( WBISHOP, WHORSE, -bishop ); + Xor( to, BB_W_HDK ); + SetClear( BB_W_BH ); break; + default: assert( ipiece_move == rook ); + NocapPro( WROOK, WDRAGON, -rook ); + Xor( to, BB_W_HDK ); + SetClear( BB_W_RD ); break; + } + else switch ( ipiece_move ) + { + case pawn: NocapNopro( WPAWN, -pawn ); + Xor( to+nfile, BB_WPAWN_ATK ); + Xor( to, BB_WPAWN_ATK ); break; + case lance: NocapNopro( WLANCE, -lance ); break; + case knight: NocapNopro( WKNIGHT, -knight ); break; + case silver: NocapNopro( WSILVER, -silver ); break; + case gold: NocapNopro( WGOLD, -gold ); + SetClear( BB_WTGOLD ); break; + case bishop: NocapNopro( WBISHOP, -bishop ); + SetClear( BB_W_BH ); break; + case rook: NocapNopro( WROOK, -rook ); + SetClear( BB_W_RD ); break; + case king: BOARD[from] = -king; + SQ_WKING = (char)from; + SetClear( BB_W_HDK ); break; + case pro_pawn: NocapNopro( WPRO_PAWN, -pro_pawn ); + SetClear( BB_WTGOLD ); break; + case pro_lance: NocapNopro( WPRO_LANCE, -pro_lance ); + SetClear( BB_WTGOLD ); break; + case pro_knight: NocapNopro( WPRO_KNIGHT,-pro_knight ); + SetClear( BB_WTGOLD ); break; + case pro_silver: NocapNopro( WPRO_SILVER, -pro_silver ); + SetClear( BB_WTGOLD ); break; + case horse: NocapNopro( WHORSE, -horse ); + SetClear( BB_W_HDK ); + SetClear( BB_W_BH ); break; + default: assert( ipiece_move == dragon ); + NocapNopro( WDRAGON, -dragon ); + SetClear( BB_W_HDK ); + SetClear( BB_W_RD ); break; + } + + if ( ipiece_cap ) + { + switch( ipiece_cap ) + { + case pawn: CapB( PAWN, pawn, pawn ); + Xor( to-nfile, BB_BPAWN_ATK ); break; + case lance: CapB( LANCE, lance, lance ); break; + case knight: CapB( KNIGHT, knight, knight ); break; + case silver: CapB( SILVER, silver, silver ); break; + case gold: CapB( GOLD, gold, gold ); + Xor( to, BB_BTGOLD ); break; + case bishop: CapB( BISHOP, bishop, bishop ); + Xor( to, BB_B_BH ); break; + case rook: CapB( ROOK, rook, rook ); + Xor( to, BB_B_RD ); break; + case pro_pawn: CapB( PRO_PAWN, pawn, pro_pawn ); + Xor( to, BB_BTGOLD ); break; + case pro_lance: CapB( PRO_LANCE, lance, pro_lance ); + Xor( to, BB_BTGOLD ); break; + case pro_knight: CapB( PRO_KNIGHT, knight, pro_knight ); + Xor( to, BB_BTGOLD ); break; + case pro_silver: CapB( PRO_SILVER, silver, pro_silver ); + Xor( to, BB_BTGOLD ); break; + case horse: CapB( HORSE, bishop, horse ); + Xor( to, BB_B_HDK ); + Xor( to, BB_B_BH ); break; + default: assert( ipiece_cap == dragon ); + CapB( DRAGON, rook, dragon); + Xor( to, BB_B_HDK ); + Xor( to, BB_B_RD ); break; + } + Xor( to, BB_BOCCUPY ); + XorFile( from, OCCUPIED_FILE ); + XorDiag1( from, OCCUPIED_DIAG1 ); + XorDiag2( from, OCCUPIED_DIAG2 ); + } + else { + BOARD[to] = empty; + SetClearFile( from, to, OCCUPIED_FILE ); + SetClearDiag1( from, to, OCCUPIED_DIAG1 ); + SetClearDiag2( from, to, OCCUPIED_DIAG2 ); + } + } + + assert( exam_bb( ptree ) ); +} + + +#undef CapW +#undef CapB +#undef NocapNopro +#undef NocapPro diff --git a/utility.c b/utility.c new file mode 100644 index 0000000..75b1b9a --- /dev/null +++ b/utility.c @@ -0,0 +1,689 @@ +#include +#include +#include +#include +#include +#include +#include "shogi.h" + + +int +ini_game( tree_t * restrict ptree, const min_posi_t *pmin_posi, int flag, + const char *str_name1, const char *str_name2 ) +{ + bitboard_t bb; + int piece; + int sq, iret; + + if ( flag & flag_history ) + { + iret = open_history( str_name1, str_name2 ); + if ( iret < 0 ) { return iret; } + } + + if ( ! ( flag & flag_nofmargin ) ) + { + fmg_misc = FMG_MISC; + fmg_cap = FMG_CAP; + fmg_drop = FMG_DROP; + fmg_mt = FMG_MT; + fmg_misc_king = FMG_MISC_KING; + fmg_cap_king = FMG_CAP_KING; + } + + memcpy( ptree->posi.asquare, pmin_posi->asquare, nsquare ); + ptree->move_last[0] = ptree->amove; + ptree->nsuc_check[0] = 0; + ptree->nsuc_check[1] = 0; + root_nrep = 0; + root_turn = pmin_posi->turn_to_move; + HAND_B = pmin_posi->hand_black; + HAND_W = pmin_posi->hand_white; + MATERIAL = 0; + + BBIni( BB_BOCCUPY ); + BBIni( BB_BPAWN ); + BBIni( BB_BLANCE ); + BBIni( BB_BKNIGHT ); + BBIni( BB_BSILVER ); + BBIni( BB_BGOLD ); + BBIni( BB_BBISHOP ); + BBIni( BB_BROOK ); + BBIni( BB_BPRO_PAWN ); + BBIni( BB_BPRO_LANCE ); + BBIni( BB_BPRO_KNIGHT ); + BBIni( BB_BPRO_SILVER ); + BBIni( BB_BHORSE ); + BBIni( BB_BDRAGON ); + BBIni( BB_BTGOLD ); + BBIni( BB_WOCCUPY ); + BBIni( BB_WPAWN ); + BBIni( BB_WLANCE ); + BBIni( BB_WKNIGHT ); + BBIni( BB_WSILVER ); + BBIni( BB_WGOLD ); + BBIni( BB_WBISHOP ); + BBIni( BB_WROOK ); + BBIni( BB_WPRO_PAWN ); + BBIni( BB_WPRO_LANCE ); + BBIni( BB_WPRO_KNIGHT ); + BBIni( BB_WPRO_SILVER ); + BBIni( BB_WHORSE ); + BBIni( BB_WDRAGON ); + BBIni( BB_WTGOLD ); + BBIni( OCCUPIED_FILE ); + BBIni( OCCUPIED_DIAG1 ); + BBIni( OCCUPIED_DIAG2 ); + + for ( sq = 0; sq < nsquare; sq++ ) { + piece = BOARD[sq]; + if ( piece > 0 ) { + Xor( sq, BB_BOCCUPY ); + XorFile( sq, OCCUPIED_FILE ); + XorDiag1( sq, OCCUPIED_DIAG1 ); + XorDiag2( sq, OCCUPIED_DIAG2 ); + switch ( piece ) + { + case pawn: Xor( sq, BB_BPAWN ); break; + case lance: Xor( sq, BB_BLANCE ); break; + case knight: Xor( sq, BB_BKNIGHT ); break; + case silver: Xor( sq, BB_BSILVER ); break; + case rook: Xor( sq, BB_BROOK ); break; + case bishop: Xor( sq, BB_BBISHOP ); break; + case king: SQ_BKING = (char)sq; break; + case dragon: Xor( sq, BB_BDRAGON ); break; + case horse: Xor( sq, BB_BHORSE ); break; + case gold: Xor( sq, BB_BGOLD ); break; + case pro_pawn: Xor( sq, BB_BPRO_PAWN ); break; + case pro_lance: Xor( sq, BB_BPRO_LANCE ); break; + case pro_knight: Xor( sq, BB_BPRO_KNIGHT ); break; + case pro_silver: Xor( sq, BB_BPRO_SILVER ); break; + } + } + else if ( piece < 0 ) { + Xor( sq, BB_WOCCUPY ); + XorFile( sq, OCCUPIED_FILE ); + XorDiag1( sq, OCCUPIED_DIAG1 ); + XorDiag2( sq, OCCUPIED_DIAG2 ); + switch ( - piece ) + { + case pawn: Xor( sq, BB_WPAWN ); break; + case lance: Xor( sq, BB_WLANCE ); break; + case knight: Xor( sq, BB_WKNIGHT ); break; + case silver: Xor( sq, BB_WSILVER ); break; + case rook: Xor( sq, BB_WROOK ); break; + case bishop: Xor( sq, BB_WBISHOP ); break; + case king: SQ_WKING = (char)sq; break; + case dragon: Xor( sq, BB_WDRAGON ); break; + case horse: Xor( sq, BB_WHORSE ); break; + case gold: Xor( sq, BB_WGOLD ); break; + case pro_pawn: Xor( sq, BB_WPRO_PAWN ); break; + case pro_lance: Xor( sq, BB_WPRO_LANCE ); break; + case pro_knight: Xor( sq, BB_WPRO_KNIGHT ); break; + case pro_silver: Xor( sq, BB_WPRO_SILVER ); break; + } + } + } + + BBOr( BB_BTGOLD, BB_BPRO_PAWN, BB_BGOLD ); + BBOr( BB_BTGOLD, BB_BPRO_LANCE, BB_BTGOLD ); + BBOr( BB_BTGOLD, BB_BPRO_KNIGHT, BB_BTGOLD ); + BBOr( BB_BTGOLD, BB_BPRO_SILVER, BB_BTGOLD ); + BBOr( BB_B_HDK, BB_BHORSE, BB_BDRAGON ); + BBOr( BB_B_HDK, BB_BKING, BB_B_HDK ); + BBOr( BB_B_BH, BB_BBISHOP, BB_BHORSE ); + BBOr( BB_B_RD, BB_BROOK, BB_BDRAGON ); + + BBOr( BB_WTGOLD, BB_WPRO_PAWN, BB_WGOLD ); + BBOr( BB_WTGOLD, BB_WPRO_LANCE, BB_WTGOLD ); + BBOr( BB_WTGOLD, BB_WPRO_KNIGHT, BB_WTGOLD ); + BBOr( BB_WTGOLD, BB_WPRO_SILVER, BB_WTGOLD ); + BBOr( BB_W_HDK, BB_WHORSE, BB_WDRAGON ); + BBOr( BB_W_HDK, BB_WKING, BB_W_HDK ); + BBOr( BB_W_BH, BB_WBISHOP, BB_WHORSE ); + BBOr( BB_W_RD, BB_WROOK, BB_WDRAGON ); + + BB_BPAWN_ATK.p[0] = ( BB_BPAWN.p[0] << 9 ) & 0x7ffffffU; + BB_BPAWN_ATK.p[0] |= ( BB_BPAWN.p[1] >> 18 ) & 0x00001ffU; + BB_BPAWN_ATK.p[1] = ( BB_BPAWN.p[1] << 9 ) & 0x7ffffffU; + BB_BPAWN_ATK.p[1] |= ( BB_BPAWN.p[2] >> 18 ) & 0x00001ffU; + BB_BPAWN_ATK.p[2] = ( BB_BPAWN.p[2] << 9 ) & 0x7ffffffU; + + BB_WPAWN_ATK.p[2] = ( BB_WPAWN.p[2] >> 9 ); + BB_WPAWN_ATK.p[2] |= ( BB_WPAWN.p[1] << 18 ) & 0x7fc0000U; + BB_WPAWN_ATK.p[1] = ( BB_WPAWN.p[1] >> 9 ); + BB_WPAWN_ATK.p[1] |= ( BB_WPAWN.p[0] << 18 ) & 0x7fc0000U; + BB_WPAWN_ATK.p[0] = ( BB_WPAWN.p[0] >> 9 ); + + MATERIAL = eval_material( ptree ); + HASH_KEY = hash_func( ptree ); + + memset( ptree->hist_good, 0, sizeof(ptree->hist_good) ); + memset( ptree->hist_tried, 0, sizeof(ptree->hist_tried) ); + memset( hash_rejections_parent, 0, sizeof(hash_rejections_parent) ); + memset( hash_rejections, 0, sizeof(hash_rejections) ); + + game_status &= ( flag_quiet | flag_reverse | flag_narrow_book + | flag_time_extendable | flag_learning + | flag_nobeep | flag_nostress | flag_nopeek + | flag_noponder | flag_noprompt ); + + sec_b_total = 0; + sec_w_total = 0; + sec_elapsed = 0; + last_root_value = 0; + n_nobook_move = 0; + last_pv.depth = 0; + last_pv.length = 0; + last_pv.a[0] = 0; + last_pv.a[1] = 0; + + if ( InCheck( root_turn ) ) + { + ptree->nsuc_check[1] = 1U; + if ( is_mate( ptree, 1 ) ) { game_status |= flag_mated; } + } + + BBOr( bb, BB_BPAWN, BB_WPAWN ); + BBOr( bb, bb, BB_BPRO_PAWN ); + BBOr( bb, bb, BB_WPRO_PAWN ); + npawn_box = npawn_max; + npawn_box -= PopuCount( bb ); + npawn_box -= (int)I2HandPawn(HAND_B); + npawn_box -= (int)I2HandPawn(HAND_W); + + BBOr( bb, BB_BLANCE, BB_WLANCE ); + BBOr( bb, bb, BB_BPRO_LANCE ); + BBOr( bb, bb, BB_WPRO_LANCE ); + nlance_box = nlance_max; + nlance_box -= PopuCount( bb ); + nlance_box -= (int)I2HandLance(HAND_B); + nlance_box -= (int)I2HandLance(HAND_W); + + BBOr( bb, BB_BKNIGHT, BB_WKNIGHT ); + BBOr( bb, bb, BB_BPRO_KNIGHT ); + BBOr( bb, bb, BB_WPRO_KNIGHT ); + nknight_box = nknight_max; + nknight_box -= PopuCount( bb ); + nknight_box -= (int)I2HandKnight(HAND_B); + nknight_box -= (int)I2HandKnight(HAND_W); + + BBOr( bb, BB_BSILVER, BB_WSILVER ); + BBOr( bb, bb, BB_BPRO_SILVER ); + BBOr( bb, bb, BB_WPRO_SILVER ); + nsilver_box = nsilver_max; + nsilver_box -= PopuCount( bb ); + nsilver_box -= (int)I2HandSilver(HAND_B); + nsilver_box -= (int)I2HandSilver(HAND_W); + + BBOr( bb, BB_BGOLD, BB_WGOLD ); + ngold_box = ngold_max; + ngold_box -= PopuCount( bb ); + ngold_box -= (int)I2HandGold(HAND_B); + ngold_box -= (int)I2HandGold(HAND_W); + + BBOr( bb, BB_BBISHOP, BB_WBISHOP ); + BBOr( bb, bb, BB_BHORSE ); + BBOr( bb, bb, BB_WHORSE ); + nbishop_box = nbishop_max; + nbishop_box -= PopuCount( bb ); + nbishop_box -= (int)I2HandBishop(HAND_B); + nbishop_box -= (int)I2HandBishop(HAND_W); + + BBOr( bb, BB_BROOK, BB_WROOK ); + BBOr( bb, bb, BB_BDRAGON ); + BBOr( bb, bb, BB_WDRAGON ); + nrook_box = nrook_max; + nrook_box -= PopuCount( bb ); + nrook_box -= (int)I2HandRook(HAND_B); + nrook_box -= (int)I2HandRook(HAND_W); + + iret = exam_tree( ptree ); + if ( iret < 0 ) + { + ini_game( ptree, &min_posi_no_handicap, 0, NULL, NULL ); + return iret; + } + + return 1; +} + + +int +gen_legal_moves( tree_t * restrict ptree, unsigned int *p0 ) +{ + unsigned int *p1; + int i, j, n; + + p1 = GenCaptures( root_turn, p0 ); + p1 = GenNoCaptures( root_turn, p1 ); + p1 = GenCapNoProEx2( root_turn, p1 ); + p1 = GenNoCapNoProEx2( root_turn, p1 ); + p1 = GenDrop( root_turn, p1 ); + n = (int)( p1 - p0 ); + + for ( i = 0; i < n; i++ ) + { + MakeMove( root_turn, p0[i], 1 ); + if ( InCheck( root_turn ) ) + { + UnMakeMove( root_turn, p0[i], 1 ); + p0[i] = 0; + continue; + } + if ( InCheck(Flip(root_turn)) ) + { + ptree->nsuc_check[2] = (unsigned char)( ptree->nsuc_check[0] + 1U ); + if ( ptree->nsuc_check[2] >= 6U + && ( detect_repetition( ptree, 2, Flip(root_turn), 3 ) + == perpetual_check ) ) + { + UnMakeMove( root_turn, p0[i], 1 ); + p0[i] = 0; + continue; + } + } + UnMakeMove( root_turn, p0[i], 1 ); + } + + for ( i = 0; i < n; ) + { + if ( ! p0[i] ) + { + for ( j = i+1; j < n; j++ ) { p0[j-1] = p0[j]; } + n -= 1; + } + else { i++; } + } + + return n; +} + + +/* + - detection of perpetual check is omitted. + - weak moves are omitted. +*/ +int +is_mate( tree_t * restrict ptree, int ply ) +{ + int iret = 0; + + assert( InCheck(root_turn) ); + + ptree->move_last[ply] = GenEvasion( root_turn, ptree->move_last[ply-1] ); + if ( ptree->move_last[ply] == ptree->move_last[ply-1] ) { iret = 1; } + + return iret; +} + + +int +is_hand_eq_supe( unsigned int u, unsigned int uref ) +{ +#if 1 +/* aggressive superior correspondences are applied, that is: + * pawn <= lance, silver, gold, rook + * lance <= rook. + */ + int nsupe; + + if ( IsHandKnight(u) < IsHandKnight(uref) + || IsHandSilver(u) < IsHandSilver(uref) + || IsHandGold(u) < IsHandGold(uref) + || IsHandBishop(u) < IsHandBishop(uref) + || IsHandRook(u) < IsHandRook(uref) ) { return 0; } + + nsupe = (int)I2HandRook(u) - (int)I2HandRook(uref); + nsupe += (int)I2HandLance(u) - (int)I2HandLance(uref); + if ( nsupe < 0 ) { return 0; } + + nsupe += (int)I2HandSilver(u) - (int)I2HandSilver(uref); + nsupe += (int)I2HandGold(u) - (int)I2HandGold(uref); + nsupe += (int)I2HandPawn(u) - (int)I2HandPawn(uref); + if ( nsupe < 0 ) { return 0; } + + return 1; +#else + if ( IsHandPawn(u) >= IsHandPawn(uref) + && IsHandLance(u) >= IsHandLance(uref) + && IsHandKnight(u) >= IsHandKnight(uref) + && IsHandSilver(u) >= IsHandSilver(uref) + && IsHandGold(u) >= IsHandGold(uref) + && IsHandBishop(u) >= IsHandBishop(uref) + && IsHandRook(u) >= IsHandRook(uref) ) { return 1; } + + return 0; +#endif +} + + +/* weak moves are omitted. */ +int +detect_repetition( tree_t * restrict ptree, int ply, int turn, int nth ) +{ + const unsigned int *p; + unsigned int hand1, hand2; + int n, i, imin, counter, irep, ncheck; + + ncheck = (int)ptree->nsuc_check[ply]; + n = root_nrep + ply - 1; + + /*if ( ncheck >= 6 )*/ + if ( ncheck >= nth * 2 ) + { + /* imin = n - ncheck*2; */ + imin = n - ncheck*2 + 1; + if ( imin < 0 ) { imin = 0; } + + ptree->move_last[ply] = GenEvasion( turn, ptree->move_last[ply-1] ); + for ( p = ptree->move_last[ply-1]; p < ptree->move_last[ply]; p++ ) + { + MakeMove( turn, *p, ply ); + + /* for ( i = n-1, counter = 0; i >= imin; i -= 2 ) */ + for ( i = n-3, counter = 0; i >= imin; i -= 2 ) + { + if ( ptree->rep_board_list[i] == HASH_KEY + && ptree->rep_hand_list[i] == HAND_B + && ++counter == nth ) + /* && ncheck*2 - 1 >= n - i )*/ + { + UnMakeMove( turn, *p, ply ); + move_evasion_pchk = *p; + return perpetual_check; + } + } + UnMakeMove( turn, *p, ply ); + } + } + + irep = no_rep; + for ( i = n-4, counter = 0; i >= 0; i-- ) + { + if ( ptree->rep_board_list[i] == HASH_KEY ) + { + hand1 = HAND_B; + hand2 = ptree->rep_hand_list[i]; + + if ( (n-i) & 1 ) + { + if ( irep == no_rep ) + { + if ( turn ) + { + if ( is_hand_eq_supe( hand2, hand1 ) ) + { + irep = white_superi_rep; + } + } + else if ( is_hand_eq_supe( hand1, hand2 ) ) + { + irep = black_superi_rep; + } + } + } + else if ( hand1 == hand2 ) + { + if ( ++counter == nth ) + { + if ( (ncheck-1)*2 >= n - i ) { return perpetual_check; } + else { return four_fold_rep; } + } + } + else if ( irep == no_rep ) + { + if ( is_hand_eq_supe( hand1, hand2 ) ) + { + irep = black_superi_rep; + } + else if ( is_hand_eq_supe( hand2, hand1 ) ) + { + irep = white_superi_rep; + } + } + } + } + + return irep; +} + + +int +com_turn_start( tree_t * restrict ptree, int flag ) +{ + const char *str_move; + unsigned int move, sec_total; + int iret, is_resign, value, ply; + + if ( ! ( flag & flag_from_ponder ) ) + { + assert( ! ( game_status & mask_game_end ) ); + + time_start = time_turn_start; + + game_status |= flag_thinking; + iret = iterate( ptree, flag ); + game_status &= ~flag_thinking; + if ( iret < 0 ) { return iret; } + } + if ( game_status & flag_suspend ) { return 1; } + + move = last_pv.a[1]; + value = root_turn ? -last_root_value : last_root_value; + str_move = str_CSA_move( move ); + + if ( value < -resign_threshold && last_pv.type != pv_fail_high ) + { +#if defined(DEKUNOBOU) + if ( dek_ngame ) + { + dek_lost += 1; + Out( "Bonanza lost against Dekunobou\n" ); + } +#endif + is_resign = 1; + } + else { + is_resign = 0; + +#if defined(DEKUNOBOU) + if ( dek_ngame && ! is_resign + && value > ( MT_CAP_DRAGON * 3 ) / 2 + && value > resign_threshold + && value != score_inferior ) + { + is_resign = 1; + dek_win += 1; + Out( "Bonanza won against Dekunobou.\n" ); + } + if ( dek_ngame && ! is_resign && value == -score_draw ) + { + iret = make_move_root( ptree, move, ( flag_rep | flag_nomake_move ) ); + if ( iret < 0 ) + { + Out( "%s\n\n", str_move ); + return iret; + } + else if ( iret == 2 ) + { + is_resign = 1; + Out( "The game with Dekunobou is drawn.\n" ); + } + } + if ( dek_ngame && ! is_resign && record_game.moves > 255 ) + { + is_resign = 1; + Out( "The game with Dekunobou is interrupted...\n" ); + } +#endif + } + +#if defined(DBG_EASY) + if ( easy_move && easy_move != move ) + { + out_warning( "EASY MOVE DITECTION FAILED." ); + } +#endif + + /* send urgent outputs */ + if ( is_resign ) + { +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) + { + iret = sckt_out( sckt_csa, "%%TORYO\n" ); + if ( iret < 0 ) { return iret; } + } +#endif + OutCsaShogi( "resign\n" ); + OutDek( "%%TORYO\n" ); + } + else { +#if defined(CSA_LAN) + if ( sckt_csa != SCKT_NULL ) + { + iret = sckt_out( sckt_csa, "%c%s\n", ach_turn[root_turn], str_move ); + if ( iret < 0 ) { return iret; } + } +#endif + + OutCsaShogi( "move%s\n", str_move ); + OutDek( "%c%s\n", ach_turn[root_turn], str_move ); + } + OutBeep(); + + /* learning and stuff */; + ply = record_game.moves; + if ( ply < HASH_REG_HIST_LEN ) + { + history_book_learn[ply].data &= ~( (1U<<31) | 0xffffU ); + history_book_learn[ply].data |= (unsigned int)(value+32768); + history_book_learn[ply].move_responsible = move; + history_book_learn[ply].key_responsible = (unsigned int)HASH_KEY; + history_book_learn[ply].hand_responsible = (unsigned int)HAND_B; + } + + iret = hash_learn( ptree, move, value, iteration_depth - 1 ); + if ( iret < 0 ) { return iret; } + + /* show search result and make a move */ + if ( is_resign ) + { + show_prompt(); + game_status |= flag_resigned; + renovate_time( root_turn ); + out_CSA( ptree, &record_game, MOVE_RESIGN ); + sec_total = root_turn ? sec_w_total : sec_b_total; + str_move = "resign"; + } + else { + show_prompt(); + iret = make_move_root( ptree, move, + ( flag_rep | flag_time | flag_history + | flag_rejections ) ); + if ( iret < 0 ) + { + Out( "%s\n\n", str_move ); + return iret; + } + sec_total = root_turn ? sec_b_total : sec_w_total; + } + + OutCsaShogi( "info tt %03u:%02u\n", sec_total / 60U, sec_total % 60U ); + Out( "%s '(%d%s) %03u:%02u/%03u:%02u elapsed: b%u, w%u\n", + str_move, value, + ( last_pv.type == pv_fail_high ) ? "!" : "", + sec_elapsed / 60U, sec_elapsed % 60U, + sec_total / 60U, sec_total % 60U, + sec_b_total, sec_w_total ); + + if ( ! is_resign ) + { +#if ! defined(NO_STDOUT) + iret = out_board( ptree, stdout, move, 0 ); + if ( iret < 0 ) { return iret; } +#endif + } + + return 1; +} + + +#if defined(MNJ_LAN) +int mnj_reset_tbl( int sd, unsigned int seed ) +{ + double average, deviation, d; + unsigned int u; + int i, j; + + if ( clear_trans_table() < 0 ) { return -1; } + ehash_clear(); + + if ( sd <= 0 ) + { + for ( i = 0; i < MNJ_MASK + 1; i++ ) { mnj_tbl[i] = 0; } + return 1; + } + + ini_rand( seed ); + + for( i = 0; i < MNJ_MASK + 1; i++ ) + { + d = -6.0; + + for ( j = 0; j < 12; j++ ) { d += (double)rand32() / (double)UINT_MAX; } + mnj_tbl[i] = (short)( (double)sd * d ); + } + + average = 0.0; + for ( i = 0; i < MNJ_MASK + 1; i++ ) { average += (double)mnj_tbl[i]; } + average /= (double)( MNJ_MASK + 1 ); + + deviation = 0.0; + for ( i = 0; i < MNJ_MASK + 1; i++ ) + { + d = (double)mnj_tbl[i] - average; + deviation += d * d; + } + deviation = sqrt( deviation / (double)( MNJ_MASK + 1 ) ); + + if ( get_elapsed( &u ) < 0 ) { return -1; } + ini_rand( u ); + + Out( "\nThe normal distribution N(0,sd^2) is generated.\n" ); + Out( " actual average: % .3f\n", average ); + Out( " actual standard deviation: % .3f\n", deviation ); + Out( "rand seed = %x\n", u ); + + return 1; +} +#endif + + +void * +memory_alloc( size_t nbytes ) +{ +#if defined(_WIN32) + void *p = VirtualAlloc( NULL, nbytes, MEM_COMMIT, PAGE_READWRITE ); + if ( p == NULL ) { str_error = "VirturlAlloc() faild"; } +#else + void *p = malloc( nbytes ); + if ( p == NULL ) { str_error = "malloc() faild"; } +#endif + return p; +} + + +int +memory_free( void *p ) +{ +#if defined(_WIN32) + if ( VirtualFree( p, 0, MEM_RELEASE ) ) { return 1; } + str_error = "VirtualFree() faild"; + return -2; +#else + free( p ); + return 1; +#endif +} diff --git a/valid.c b/valid.c new file mode 100644 index 0000000..e462c62 --- /dev/null +++ b/valid.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include "shogi.h" + + +int +is_move_valid( tree_t * restrict __ptree__, unsigned int move, int turn ) +{ + tree_t * restrict ptree = __ptree__; + int from = (int)I2From(move); + int to = (int)I2To(move); + int piece_move; + unsigned int u; + bitboard_t bb; + + if ( from < nsquare ) + { + piece_move = (int)I2PieceMove(move); + if ( turn ) + { + if ( BOARD[from] != -piece_move ) { return 0; } + if ( BOARD[to] != (int)UToCap(move) ) { return 0; } + } + else { + if ( BOARD[from] != piece_move ) { return 0; } + if ( BOARD[to] != -(int)UToCap(move) ) { return 0; } + } + + switch ( piece_move ) + { + case 0: return 0; + + case lance: case bishop: case horse: case rook: case dragon: + BBOr( bb, BB_BOCCUPY, BB_WOCCUPY ); + BBAnd( bb, bb, abb_obstacle[from][to] ); + if ( BBToU( bb ) ) { return 0; } + break; + } + + return 1; + } + + if ( BOARD[to] ) { return 0; } + else { + u = turn ? HAND_W : HAND_B; + switch ( From2Drop(from) ) + { + case pawn: + if ( ! IsHandPawn(u) ) { return 0; } + { + if ( turn ) + { + u = BBToU( BB_WPAWN_ATK ); + if ( ( mask_file1 >> aifile[to] ) & u ) { return 0; } + if ( IsMateWPawnDrop(__ptree__, to) ) { return 0; } + } + else { + u = BBToU( BB_BPAWN_ATK ); + if ( ( mask_file1 >> aifile[to] ) & u ) { return 0; } + if ( IsMateBPawnDrop(__ptree__, to) ) { return 0; } + } + return 1; + } + case lance: if ( IsHandLance(u) ) { return 1; } break; + case knight: if ( IsHandKnight(u) ) { return 1; } break; + case silver: if ( IsHandSilver(u) ) { return 1; } break; + case gold: if ( IsHandGold(u) ) { return 1; } break; + case bishop: if ( IsHandBishop(u) ) { return 1; } break; + default: assert( From2Drop(from) == rook ); + if ( IsHandRook(u) ) { return 1; } break; + } + } + return 0; +} + + +#define NpchkReturn(piece) if ( (n ## piece) > (n ## piece ## _max) ) { \ + str_error = "too many " # piece "s"; \ + return -2; } + +int +exam_tree( const tree_t * restrict ptree ) +{ + int npawn, nlance, nknight, nsilver, ngold, nbishop, nrook; + int nwking, nbking, isquare, ifile, irank, wcounter, bcounter; + + /* total number of each piece */ + npawn = (int)(I2HandPawn( HAND_B ) + I2HandPawn( HAND_W )); + nlance = (int)(I2HandLance( HAND_B ) + I2HandLance( HAND_W )); + nknight = (int)(I2HandKnight( HAND_B ) + I2HandKnight( HAND_W )); + nsilver = (int)(I2HandSilver( HAND_B ) + I2HandSilver( HAND_W )); + ngold = (int)(I2HandGold( HAND_B ) + I2HandGold( HAND_W )); + nbishop = (int)(I2HandBishop( HAND_B ) + I2HandBishop( HAND_W )); + nrook = (int)(I2HandRook( HAND_B ) + I2HandRook( HAND_W )); + nwking = nbking = 0; + + for ( isquare = 0; isquare < nsquare; isquare++ ) + switch ( abs( BOARD[isquare] ) ) + { + case empty: break; + case pawn: case pro_pawn: npawn++; break; + case lance: case pro_lance: nlance++; break; + case knight: case pro_knight: nknight++; break; + case silver: case pro_silver: nsilver++; break; + case gold: ngold++; break; + case bishop: case horse: nbishop++; break; + case rook: case dragon: nrook++; break; + case king: + if ( BOARD[isquare] == king ) { nbking++; } + else { nwking++; } + break; + } + NpchkReturn( pawn ); NpchkReturn( lance ); + NpchkReturn( knight ); NpchkReturn( silver ); + NpchkReturn( gold ); NpchkReturn( bishop ); + NpchkReturn( rook ); + if ( nbking != 1 || nwking != 1 ) + { + str_error = "invalid number of kings"; + return -2; + } + + /* double pawns */ + for ( ifile = 0; ifile < 9; ifile++ ) + { + bcounter = wcounter = 0; + for ( irank = 0; irank < 9; irank++ ) + { + if ( BOARD[ irank*nfile+ifile ] == pawn ) { bcounter++; } + if ( BOARD[ irank*nfile+ifile ] == -pawn ) { wcounter++; } + } + if ( bcounter > 1 ) + { + str_error = "two black pawns at a file"; + return -2; + } + if ( wcounter > 1 ) + { + str_error="two white pawns at a file"; + return -2; + } + } + + /* pieces can not move */ + for ( isquare = 0; isquare < 9; isquare++ ) + { + if ( BOARD[ isquare ] == pawn ) + { + str_error = "black pawns in rank 1"; + return -2; + } + if ( BOARD[ isquare ] == lance ) + { + str_error = "black lances in rank 1"; + return -2; + } + } + for ( isquare = 0; isquare < 18; isquare++ ) + if ( BOARD[ isquare ] == knight ) + { + str_error = "black knights in rank 1-2"; + return -2; + } + + for ( isquare = 72; isquare < 81; isquare++ ) + { + if ( BOARD[ isquare ] == -pawn ) + { + str_error = "white pawns in rank 9"; + return -2; + } + if ( BOARD[ isquare ] == -lance ) + { + str_error = "white lances in rank 9"; + return -2; + } + } + for ( isquare = 63; isquare < 81; isquare++ ) + if ( BOARD[ isquare ] == -knight ) + { + str_error = "white knights in rank 8-9"; + return -2; + } + + if ( InCheck( Flip(root_turn) ) ) + { + str_error = str_king_hang; + return -2; + } + + assert( exam_bb( ptree ) ); + + return 1; +} + +#undef NpchkReturn -- 1.7.0.4