From: H.G. Muller Date: Wed, 20 Oct 2010 17:52:29 +0000 (+0200) Subject: Check in Bonanza Feliz 0.0 X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=18b507e1b20fc6c32ee50f00fb910a59110c1a1d;p=bonanza.git Check in Bonanza Feliz 0.0 --- 18b507e1b20fc6c32ee50f00fb910a59110c1a1d 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 0000000..fc71e77 Binary files /dev/null and b/bonanza.ico differ 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