pax_global_header00006660000000000000000000000064135004041540014506gustar00rootroot0000000000000052 comment=df26f4a7493554971440503ae788686fdcd4cf75 hachu-0.21-29-gdf26f4a/000077500000000000000000000000001350040415400142515ustar00rootroot00000000000000hachu-0.21-29-gdf26f4a/.gitignore000066400000000000000000000000211350040415400162320ustar00rootroot00000000000000hachu hachu.6.gz hachu-0.21-29-gdf26f4a/Makefile000066400000000000000000000031751350040415400157170ustar00rootroot00000000000000srcdir = . CC?=gcc CFLAGS?= -O2 -s -Wall -Wno-parentheses prefix=/usr/local DATADIR=`xboard --show-config Datadir` ALL= hachu hachu.6.gz all: ${ALL} hachu: hachu.c $(CC) $(CPPFLAGS) $(CFLAGS) hachu.c $(LDFLAGS) -o hachu install: ${ALL} ${srcdir}/svg/* install -d -m0755 $(DESTDIR)$(prefix)/games cp -u ${srcdir}/hachu $(DESTDIR)$(prefix)/games install -d -m0755 $(DESTDIR)$(prefix)/share/man/man6 cp -u ${srcdir}/hachu.6.gz $(DESTDIR)$(prefix)/share/man/man6 install -d -m0755 $(DESTDIR)$(DATADIR)/themes/chu cp -u ${srcdir}/svg/*.svg $(DESTDIR)$(DATADIR)/themes/chu install -d -m0755 $(DESTDIR)$(DATADIR)/themes/conf cp -u ${srcdir}/svg/sho ${srcdir}/svg/chu $(DESTDIR)$(DATADIR)/themes/conf install -d -m0755 $(DESTDIR)/usr/share/games/plugins/logos cp -u ${srcdir}/logo.png $(DESTDIR)/usr/share/games/plugins/logos/hachu.png install -d -m0755 $(DESTDIR)/usr/share/games/plugins/xboard cp -u ${srcdir}/hachu.eng $(DESTDIR)/usr/share/games/plugins/xboard hachu.6.gz: hachu.pod pod2man -s 6 hachu.pod | gzip -9n > hachu.6.gz clean: rm -f ${ALL} *.o dist-clean: rm -f ${ALL} *~ chu/*~ md5sums dist: install -d -m0755 HaChu install -d -m0755 HaChu/svg rm -f hachu.tar hachu.tar.gz cp hachu.c hachu.pod Makefile hachu.eng logo.png HaChu cp chu/* HaChu/svg (md5sum HaChu/* HaChu/svg/* > HaChu/md5sums) || true tar -cvvf hachu.tar HaChu gzip hachu.tar rm HaChu/svg/* rmdir HaChu/svg rm HaChu/* rmdir HaChu uninstall: rm -f $(DESTDIR)$(prefix)/share/man/man6/hachu.6.gz rm -f $(DESTDIR)$(prefix)/games/hachu rm -f $(DESTDIR)/usr/share/games/plugins/logos/hachu.png rm -f $(DESTDIR)/usr/share/games/plugins/xboard/hachu.eng hachu-0.21-29-gdf26f4a/hachu.c000066400000000000000000004054561350040415400155230ustar00rootroot00000000000000/**************************************************************************/ /* HaChu */ /* A WinBoard engine for Chu Shogi (and some related games) by H.G.Muller */ /**************************************************************************/ /* This source code is released in the public domain */ /**************************************************************************/ // TODO: // in GenCapts we do not generate jumps of more than two squares yet // promotions by pieces with Lion power stepping in & out the zone in same turn // promotion on capture #define VERSION "0.21" //define PATH level==0 || path[0] == 0x82906b && (level==1 || path[1] == 0x8790d9 && (level == 2 || path[2] == 0x8598ca && (level == 3 /*|| path[3] == 0x3e865 && (level == 4 || path[4] == 0x4b865 && (level == 5))*/))) #define PATH 0 #define HASH #define KILLERS #define NULLMOVE #define CHECKEXT #define LMR 4 #define LIONTRAP #define XWINGS #define KINGSAFETY #define KSHIELD #define FORTRESS #define PAWNBLOCK #define TANDEM 100 /* bonus for pairs of attacking light steppers */ #define KYLIN 100 /* extra end-game value of Kylin for promotability */ #define PROMO 0 /* extra bonus for 'vertical' piece when it actually promotes (diagonal pieces get half) */ #include #include #include #include #include #include #ifdef WIN32 # include int InputWaiting() { // checks for waiting input in pipe static int pipe, init; static HANDLE inp; DWORD cnt; if(!init) inp = GetStdHandle(STD_INPUT_HANDLE); if(!PeekNamedPipe(inp, NULL, 0, NULL, &cnt, NULL)) return 1; return cnt; } #else # include # include int InputWaiting() { int cnt; if(ioctl(0, FIONREAD, &cnt)) return 1; return cnt; } int GetTickCount() // with thanks to Tord { struct timeval t; gettimeofday(&t, NULL); return t.tv_sec*1000 + t.tv_usec/1000; } #endif #define BW bWidth #define BH bHeight #define BHMAX 16 #define BWMAX (2*BHMAX) #define BSIZE BWMAX*BHMAX #define ZONE zone #define ONE 1 /* currently no variants with 10-deep board */ #define BLACK 0 #define WHITE 1 #define EMPTY 0 #define EDGE (1<<11) #define TYPE (WHITE|BLACK|EDGE) #define ABSENT 2047 #define INF 8000 #define NPIECES EDGE+1 /* length of piece list */ #define SQLEN 11 /* bits in square number */ #define SQUARE ((1<1 = (limited) range // Attack table: // A table in board format, containing pairs of consecutive integers for each square (indexed as 2*sqr and 2*sqr+1) // The first integer contains info on black attacks to the square, the second similarly for white attacks // Each integer contains eight 3-bit fields, which count the number of attacks on it with moves in a particular direction // (If there are attacks by range-jumpers, the 3-bit count is increased by 2 over the actual value) // Board: // The board has twice as many files as actually used, in 0x88 fashion // The used squares hold the piece numbers (for use as index in the piece list) // Unused squares are set to the invalid piece number EDGE // There are also 3 guard ranks of EDGE on each side // Moves: // Moves are encoded as 11-bit from-square and to-square numbers packed in the low bits of an int // Special moves (like Lion double moves) are encoded by off-board to-squares above a certain value // Promotions are indicated by bit 22 // Hash table: // Entries of 16 bytes, holding a 32-bit signature, 16-bit lower- and upper-bound scores, // 8-bit draft of each of those scores, an age counter that stores the search number of the last access. // The hash key is derived as the XOR of the products pieceKey[piece]*squareKey[square]. // Promotion zones // the promoBoard contains one byte with flags for each square, to indicate for each side whether the square // is in the promotion zone (twice), on the last two ranks, or on the last rank // the promoFlag field in the piece list can select which bits of this are tested, to see if it // (1) can promote (2) can defer when the to-square is on last rank, last two ranks, or anywhere. // Pawns normally can't defer anywhere, but if the user defers with them, their promoFlag is set to promote on last rank only typedef struct { int pos; int pieceKey; int promo; int value; int pst; signed char range[8]; char promoFlag; char qval; char mobility; char mobWeight; unsigned char promoGain; char bulk; char ranking; } PieceInfo; // piece-list entry int last[2], royal[2], kylin[2]; PieceInfo p[NPIECES]; // piece list typedef struct { int lock; Move move; short int upper; short int lower; char depthU; char depthL; char flags; char age; } HashEntry; // hash-table entry // Some global variables that control your engine's behavior int ponder; int randomize; int postThinking; int noCut=1; // engine-defined option int resign; // engine-defined option int contemptFactor; // likewise int seed; int squareKey[BSIZE]; int rawBoard[BSIZE + 11*BHMAX + 6]; //int attacks[2*BSIZE]; // attack map int attackMaps[200*BSIZE], *attacks = attackMaps; unsigned char distance[2*BSIZE]; // distance table char promoBoard[BSIZE]; // flags to indicate promotion zones unsigned char rawFire[BSIZE+2*BWMAX]; // flags to indicate squares controlled by Fire Demons signed char PST[7*BSIZE]; #define board (rawBoard + 6*BHMAX + 3) #define fireBoard (rawFire + BWMAX + 1) #define dist (distance + BSIZE) PieceDesc * ListLookUp (char *name, PieceDesc *list) { // find piece of given name in list of descriptors while(list->name && strcmp(name, list->name)) list++; return (list->name == NULL ? NULL : list); } PieceDesc * LookUp (char *name, int var) { // search piece of given name in all lists relevant for given variant PieceDesc *desc; switch(var) { case V_TENJIKU: // Tenjiku desc = ListLookUp(name, tenjikuPieces); if(desc) return desc; return ListLookUp(name, chuPieces); case V_SHO: // Sho return ListLookUp(name, shoPieces); case V_DAI: // Dai desc = ListLookUp(name, daiPieces); if(desc) return desc; case V_CHU: // Chu return ListLookUp(name, chuPieces); case V_CHESS: // FIDE return ListLookUp(name, chessPieces); case V_SHATRANJ: // Shatranj return ListLookUp(name, shatranjPieces); case V_MAKRUK: // Makruk return ListLookUp(name, makrukPieces); case V_LION: // Mighty Lion return ListLookUp(name, lionPieces); case V_WA: // Wa return ListLookUp(name, waPieces); case V_WOLF: // Werewolf return ListLookUp(name, wolfPieces); case V_CASHEW: // Cashew return ListLookUp(name, ddPieces); case V_MACAD: // Cashew return ListLookUp(name, makaPieces); } return NULL; } void SqueezeOut (int n) { // remove piece number n from the mentioned side's piece list (and adapt the reference to the displaced pieces!) int i; for(i=stm+2; i<=last[stm]; i+=2) if(p[i].promo > n) p[i].promo -= 2; for(i=n; i= 0 && b >= 0) return a < b; if(a >= 0) return 1; // a (limited) range can never do the same as a special move switch(a) { case J: return b < J; // any special move is better than a plain jump case D: return b > 2 || b < D; case T: return b > 3 || b < T; case L: return b > 2 || b < D; case F: return b > 3 || b < F || b == T; case S: return b == H || b == T; case H: return b < 0; default: return 1; // a >= 0, so b must be < 0 and can always do something a ranging move cannot do } return 0; } int Lance (signed char *r) { // File-bound forward slider int i; for(i=1; i<4; i++) if(r[i] || r[i+4]) return 0; return r[0] == X; } int EasyProm (signed char *r) { if(r[0] == X) return 30 + PROMO*((unsigned int)(r[1] | r[2] | r[3] | r[5] | r[6] | r[7]) <= 1); if(r[1] == X || r[7] == X) return 30 + PROMO/2; return 0; } int IsUpwardCompatible (signed char *r, signed char *s) { int i; for(i=0; i<8; i++) { if(Worse(r[i], s[i])) return 0; } return 1; } int Forward (signed char *r) { int i; for(i=2; i<7; i++) if(r[i]) return 0; return 1; } int Range (signed char *r) { int i, m=0; for(i=0; i<8; i++) { int d = r[i]; if(r[i] < 0) d = r[i] >= L ? 2 : 36; if(d > m) m = d; } return m; } int multis[2], multiMovers[100]; void StackMultis (int col) { int i, j; multis[col] = col; for(i=col+2; i<=last[col]; i+=2) { // scan piece list for multi-capturers for(j=0; j<8; j++) if(p[i].range[j] < J && p[i].range[j] >= S || p[i].value == FVAL) { multiMovers[multis[col]] = i; // found one: put its piece number in list multis[col] += 2; break; } } } void Compactify (int stm) { // remove pieces that are permanently gone (captured or promoted) from one side's piece list int i, k; for(i=stm+2; i<=last[stm]; i+=2) { // first pass: unpromoted pieces if((k = p[i].promo) >= 0 && p[i].pos == ABSENT) { // unpromoted piece no longer there p[k].promo = -2; // orphan promoted version SqueezeOut(i); } } for(i=stm+2; i<=last[stm]; i+=2) { // second pass: promoted pieces if((k = p[i].promo) == -2 && p[i].pos == ABSENT) { // orphaned promoted piece not present SqueezeOut(i); } } StackMultis(stm); } int AddPiece (int stm, PieceDesc *list) { int i, j, *key, v; for(i=stm+2; i<=last[stm]; i += 2) { if(p[i].value < list->value || p[i].value == list->value && (p[i].promo < 0)) break; } last[stm] += 2; for(j=last[stm]; j>i; j-= 2) p[j] = p[j-2]; p[i].value = v = list->value; for(j=0; j<8; j++) p[i].range[j] = list->range[j^4*(WHITE-stm)]; switch(Range(p[i].range)) { case 1: p[i].pst = PST_STEPPER; break; case 2: p[i].pst = PST_WJUMPER; break; default: p[i].pst = PST_SLIDER; break; } key = (stm == WHITE ? &list->whiteKey : &list->blackKey); if(!*key) *key = ~(myRandom()*myRandom()); p[i].promoGain = EasyProm(list->range); // flag easy promotion based on white view p[i].pieceKey = *key; p[i].promoFlag = 0; p[i].bulk = list->bulk; p[i].ranking = list->ranking; p[i].mobWeight = v > 600 ? 0 : v >= 400 ? 1 : v >= 300 ? 2 : v > 150 ? 3 : v >= 100 ? 2 : 0; if(Lance(list->range)) p[i].mobWeight = 0, p[i].pst = list->range[4] ? PST_NEUTRAL : PST_LANCE; // keep back for(j=stm+2; j<= last[stm]; j+=2) { if(p[j].promo >= i) p[j].promo += 2; } if(royal[stm] >= i) royal[stm] += 2; if(kylin[stm] >= i) kylin[stm] += 2; if(p[i].value == (currentVariant == V_SHO || currentVariant == V_WA ? 410 : 280) ) royal[stm] = i, p[i].pst = 0; p[i].qval = (currentVariant == V_TENJIKU ? list->ranking : 0); // jump-capture hierarchy return i; } void SetUp (char *array, int var) { int i, j, n, m, color, c; char name[3], prince = 0; PieceDesc *p1, *p2; last[WHITE] = 1; last[BLACK] = 0; royal[WHITE] = royal[BLACK] = 0; for(i=BH-1; ; i--) { //printf("next rank: %s\n", array); for(j = BW*i; ; j++) { int pflag=0; if(*array == '+') pflag++, array++; c = name[0] = *array++; if(!c) goto eos; if(c == '.') continue; if(c > '0' && c <= '9') { c -= '0'; if(*array >= '0' && *array <= '9') c = 10*c + *array++ - '0'; j += c - 1; continue; } if(c == '/') break; name[1] = name[2] = 0; if(c >= 'a') { color = BLACK; c += 'A' - 'a'; } else color = WHITE; if(*array == '\'') c += 26, array++; else if(*array == '!') c += 52, array++; name[0] = IDs[2*(c - 'A')]; name[1] = IDs[2*(c - 'A') + 1]; if(name[1] == ' ') name[1] = 0; if(!strcmp(name, "CP") || pflag && !strcmp(name, "DE")) prince |= color+1; // remember if we added Crown Prince p1 = LookUp(name, var); if(!p1) printf("tellusererror Unknown piece '%s' in setup (%d)\n", name, c), exit(-1); if(pflag && p1->promoted) p1 = LookUp(p1->promoted, var); // use promoted piece instead n = AddPiece(color, p1); p[n].pos = j; if(p1->promoted[0] && !pflag) { if(!strcmp(p1->promoted, "CP")) prince |= color+1; // remember if we added Crown Prince p2 = LookUp(p1->promoted, var); m = AddPiece(color, p2); if(m <= n) n += 2; if(p2->ranking > 5) { // contageous AddPiece(color, p2); if(m <= n) n += 2; } p[n].promo = m; p[n].promoFlag = IsUpwardCompatible(p2->range, p1->range) * DONT_DEFER + CAN_PROMOTE; if(Forward(p1->range)) p[n].promoFlag |= LAST_RANK; // Pieces that only move forward can't defer on last rank if(!strcmp(p1->name, "N")) p[n].promoFlag |= CANT_DEFER; // Knights can't defer on last 2 ranks p[n].promoFlag &= n&1 ? P_WHITE : P_BLACK; p[m].promo = -1; p[m].pos = ABSENT; if(p[m].value == LVAL) kylin[color] = n; // remember piece that promotes to Lion } else p[n].promo = -1; // unpromotable piece //printf("piece = %c%-2s %d(%d) %d/%d\n", color ? 'w' : 'b', name, n, m, last[color], last[!color]); } } eos: // add dummy Kings if not yet added (needed to set royal[] to valid value!) if(!royal[WHITE]) p[AddPiece(WHITE, LookUp("K", V_CHU))].pos = ABSENT; if(!royal[BLACK]) p[AddPiece(BLACK, LookUp("K", V_CHU))].pos = ABSENT; // add dummy Crown Princes if not yet added if(!(prince & WHITE+1)) p[AddPiece(WHITE, LookUp("CP", V_CHU))].pos = ABSENT; if(!(prince & BLACK+1)) p[AddPiece(BLACK, LookUp("CP", V_CHU))].pos = ABSENT; for(i=0; i<8; i++) fireFlags[i] = 0; for(i=2, n=1; i<10; i++) if(p[i].value == FVAL) { int x = p[i].pos; // mark all burn zones fireFlags[i-2] = n; if(x != ABSENT) for(j=0; j<8; j++) fireBoard[x+kStep[j]] |= n; n <<= 1; } for(i=2; i<6; i++) if(p[i].ranking == 5) p[i].promo = -1, p[i].promoFlag = 0; // take promotability away from Werewolves for(i=0; i 0 && p[i].pst == PST_STEPPER) p[i].pst = PST_WPPROM; // use white pre-prom bonus if(j > 0 && p[i].pst == PST_STEPPER && p[i].value >= 100) p[i].pst = p[i].value <= 150 ? PST_ADVANCE : PST_WPPROM; // light steppers advance if(j > 0 && p[i].bulk == 6) p[i].pst = PST_WFLYER, p[i].mobWeight = 4; // SM defends zone if((j = p[i].promo) > 0 && g) p[i].promoGain = (p[j].value - p[i].value - g)*0.9, p[i].value = p[j].value - g; else p[i].promoGain = 0; board[p[i].pos] = i; rootEval += p[i].value + PST[p[i].pst + p[i].pos]; promoDelta += p[i].promoGain; filling += p[i].bulk; } else p[i].promoGain = 0; for(i=BLACK+2; i<=last[BLACK]; i+=2) if(p[i].pos != ABSENT) { int g = p[i].promoGain; // if(j > 0 && p[i].pst == PST_STEPPER) p[i].pst = PST_BPPROM; // use black pre-prom bonus if(j > 0 && p[i].pst == PST_STEPPER && p[i].value >= 100) p[i].pst = p[i].value <= 150 ? PST_RETRACT : PST_BPPROM; // light steppers advance if(j > 0 && p[i].pst == PST_WJUMPER) p[i].pst = PST_BJUMPER; // use black pre-prom bonus if(j > 0 && p[i].bulk == 6) p[i].pst = PST_BFLYER, p[i].mobWeight = 4; // SM defends zone if((j = p[i].promo) > 0 && g) p[i].promoGain = (p[j].value - p[i].value - g)*0.9, p[i].value = p[j].value - g; else p[i].promoGain = 0; if(i == kylin[BLACK]) p[i].promoGain = 1.25*KYLIN, p[i].value += KYLIN; board[p[i].pos] = i; rootEval -= p[i].value + PST[p[i].pst + p[i].pos]; promoDelta -= p[i].promoGain; filling += p[i].bulk; } else p[i].promoGain = 0; StackMultis(WHITE); StackMultis(BLACK); stm = WHITE; xstm = BLACK; } int myRandom() { return rand() ^ rand()>>10 ^ rand() << 10 ^ rand() << 20; } void Init (int var) { int i, j, k; PieceDesc *pawn; if(var != SAME) { // the following should be already set if we stay in same variant (for TakeBack) currentVariant = variants[var].varNr; bWidth = variants[var].boardWidth; bHeight = variants[var].boardRanks; zone = variants[var].zoneDepth; array = variants[var].array; IDs = variants[var].IDs; } bsize = bWidth*bHeight; chuFlag = (currentVariant == V_CHU || currentVariant == V_LION); tenFlag = (currentVariant == V_TENJIKU); chessFlag = (currentVariant == V_CHESS || currentVariant == V_LION || currentVariant == V_WOLF); stalemate = (currentVariant == V_CHESS || currentVariant == V_MAKRUK || currentVariant == V_LION || currentVariant == V_WOLF); repDraws = (stalemate || currentVariant == V_SHATRANJ); ll = 0; lr = bHeight - 1; ul = (bHeight - 1)*bWidth; ur = ul + bHeight - 1; pawn = LookUp("P", currentVariant); pVal = pawn ? pawn->value : 0; // get Pawn value for(i= -1; i<9; i++) { // board steps in linear coordinates kStep[i] = STEP(direction[i&7].x, direction[i&7].y); // King nStep[i] = STEP(direction[(i&7)+8].x, direction[(i&7)+8].y); // Knight } for(i=0; i<8; i++) neighbors[i+1] = kStep[i]; for(i=0; i<8; i++) { // Lion double-move decoding tables for(j=0; j<8; j++) { epList[8*i+j] = kStep[i]; toList[8*i+j] = kStep[j] + kStep[i]; for(k=0; k<8*i+j; k++) if(epList[k] == toList[8*i+j] && toList[k] == epList[8*i+j]) reverse[k] = 8*i+j, reverse[8*i+j] = k; } // Lion-Dog triple moves toList[64+i] = 3*kStep[i]; epList[64+i] = kStep[i]; toList[72+i] = 3*kStep[i]; epList[72+i] = 2*kStep[i]; toList[80+i] = 3*kStep[i]; epList[80+i] = 2*kStep[i]; ep2List[80+i] = kStep[i]; toList[88+i] = kStep[i]; epList[88+i] = 2*kStep[i]; } toList[100] = BH - 2; epList[100] = BH - 1; ep2List[100] = BH - 3; toList[100+1] = 2; epList[100+1] = 0; ep2List[100+1] = 3; toList[100+2] = bsize - BH - 2; epList[100+2] = bsize - BH - 1; ep2List[100+2] = bsize - BH - 3; toList[100+3] = bsize - BW + 2; epList[100+3] = bsize - BW; ep2List[100+3] = bsize - BW + 3; // fill distance table for(i=0; i<2*BSIZE; i++) { distance[i] = 0; } // for(i=0; i<8; i++) // for(j=1; j abs(j) ? abs(i) : abs(j); // hash key tables for(i=0; i= BH-ZONE) v |= (CAN_PROMOTE | DONT_DEFER) & P_WHITE; else v &= ~P_WHITE; if(i >= BH-2) v |= CANT_DEFER & P_WHITE; if(i == BH-1) v |= LAST_RANK & P_WHITE; promoBoard[BW*i + j] = v; } // piece-square tables for(j=0; j BH-3 ? 3 : 0) - (j == 0 || j == BH-1 ? 5 : 0) + 3*(i==zone || i==BH-zone-1); // stepper centralization PST[PST_WJUMPER+s] = d/6; // double-stepper centralization PST[PST_SLIDER +s] = d/12 - 15*(i==BH/2 || i==(BH-1)/2);// slider centralization PST[PST_TRAP +s] = j < 3 || j > BH-4 ? (i < 3 ? 7 : i == 3 ? 4 : i == 4 ? 2 : 0) : 0; PST[PST_CENTER+s] = ((BH-1)*(BH-1) - (2*i - BH + 1)*(2*i - BH + 1) - (2*j - BH + 1)*(2*j - BH + 1))/6; PST[PST_WPPROM+s] = PST[PST_BPPROM+s] = PST[PST_STEPPER+s]; // as stepper, but with pre-promotion bonus W/B PST[PST_BJUMPER+s] = PST[PST_WJUMPER+s]; // as jumper, but with pre-promotion bonus B PST[PST_ZONDIST+s] = BW*(zone - 1 - i); // board step to enter promo zone black PST[PST_ADVANCE+s] = PST[PST_WFLYER-s-1] = 2*(5*i+i*i) - (i >= zone)*6*(i-zone+1)*(i-zone+1) - (2*j - BH + 1)*(2*j - BH + 1)/BH + BH/2 - 50 - 35*(j==0 || j == BH-1) - 15*(j == 1 || BH-2); // advance-encouraging table PST[PST_WFLYER +s] = PST[PST_LANCE-s-1] = (i == zone-1)*40 + (i == zone-2)*20 - 20; PST[PST_LANCE +s] = (PST[PST_STEPPER+j] - PST[PST_STEPPER+s])/2; } if(zone > 0) PST[PST_WPPROM+BW*(BH-1-zone) + j] += 10, PST[PST_BPPROM + BW*zone + j] += 10; if(j > (BH-1)/2 - 3 && j < BH/2 + 3) PST[PST_WPPROM + j] += 4, PST[PST_BPPROM + BW*(BH-1) + j] += 4; // fortress if(j > (BH-1)/2 - 2 && j < BH/2 + 2) PST[PST_WPPROM + BW + j] += 2, PST[PST_BPPROM + BW*(BH-2) + j] += 2; // fortress #if KYLIN // pre-promotion bonuses for jumpers if(zone > 0) PST[PST_WJUMPER + BW*(BH-2-zone) + j] = PST[PST_BJUMPER + BW*(zone+1) + j] = 100, PST[PST_WJUMPER + BW*(BH-1-zone) + j] = PST[PST_BJUMPER + BW*zone + j] = 200; #endif } p[EDGE].qval = 5; // tenjiku jump-capturer sentinel } int PSTest () { int r, f, score, tot=0; for(r=0; r= d) continue; // already done if(!map[m]) moveStack[msp++] = from< 1 && board[to] == EMPTY) AreaStep(from, to, flags, m, d-1); } } void AreaMoves (int from, int piece, int range) { int i; for(i=0; i<49; i++) map[i] = 0; map[3*7+7] = range; AreaStep(from, from, p[piece].promoFlag, 3*7+3, range); } void MarkBurns (int x) { // make bitmap of squares in FI (7x7) neighborhood where opponents can be captured or burned int r=x>>5, f=x&15, top=8, bottom=0, b=0, t=8, left=0, right=8; // 9x9 area; assumes 32x16 board if(r < 4) bottom = 4 - r, rows[b=bottom-1] = 0; else if(r > 11) top = 19 - r, rows[t=top+1] = 0; // adjust area to board edges if(f < 4) left = 4 - f; else if(f > 11) right = 19 - f; for(r=bottom; r<=top; r++) { int mask = 0, y = x + 16*r; for(f=left; f <= right; f++) { if(board[y + f - (4*16+4)] != EMPTY && (board[y + f - (4*16+4)] & TYPE) == xstm) mask |= rowMask[f]; // on-rank attacks } rows[r+2] = mask; } for(r=b; r<=t-2; r++) rows[r] |= rows[r+1] | rows[r+2]; // smear vertically } void GenCastlings () { // castings for Lion Chess. Assumes board width = 8 and Kings on e-file, and K/R value = 280/300! int f = BH>>1, t = CASTLE; if(stm != WHITE) f += bsize - BW, t += 2; if(p[board[f]].value = 280) { if(p[board[f-4]].value == 300 && board[f-3] == EMPTY && board[f-2] == EMPTY && board[f-1] == EMPTY) moveStack[msp++] = f<= S) { // in any case, do a jump of 2 int occup = NewNonCapture(x, x + 2*v, pFlag); if(r < I) { // Lion power, also single step if(!NewNonCapture(x, x + v, pFlag)) nullMove = (r == W ? ABSENT : x); else occup = 1; if(r <= L) { // true Lion, also Knight jump if(!occup & r < L) for(y=x+2*v; !NewNonCapture(x, y+=v, pFlag) && r == S; ); // BS and FF moves v = nStep[j]; if(r != W) NewNonCapture(x, x + v, pFlag); } else if(r >= T) { // T or K occup |= NewNonCapture(x, x+3*v, pFlag); // Lion Dog, also triple step if(!occup && r == K) for(y=x+3*v; !NewNonCapture(x, y+=v, pFlag); ); // Teaching King distant moves } } else if(r == I) NewNonCapture(x, x + v, pFlag); // also do step } else if(r == M) { // FIDE Pawn; check double-move if(!NewNonCapture(x, x+v, pFlag) && chessFlag && promoBoard[x-v] & LAST_RANK) NewNonCapture(x, x+2*v, pFlag), moveStack[msp-1] |= DEFER; // use promoSuppress flag as e.p. flag } continue; } y = x; while(r-- > 0) if(NewNonCapture(x, y+=v, pFlag)) break; } } return nullMove; } void report (int x, int y, int i) { } int MapOneColor (int start, int last, int *map) { int i, j, totMob = 0; for(i=start+2; i<=last; i+=2) { int mob = 0; if(p[i].pos == ABSENT) continue; for(j=0; j<8; j++) { int x = p[i].pos, v = kStep[j], r = p[i].range[j]; if(r < 0) { // jumping piece, special treatment if(r == N) { x += nStep[j]; if(board[x] != EMPTY && board[x] != EDGE) map[2*x + start] += one[8]; } else if(r >= S) { // in any case, do a jump of 2 if(board[x + 2*v] != EMPTY && board[x + 2*v] != EDGE) map[2*(x + 2*v) + start] += one[j], mob += (board[x + 2*v] ^ start) & 1; if(r < J) { // more than plain jump if(board[x + v] != EMPTY && board[x + v] != EDGE) map[2*(x + v) + start] += one[j]; // single step (completes D and I) if(r < I) { // Lion power if(r >= T) { // Lion Dog, also do a jump of 3 if(board[x + 3*v] != EMPTY && board[x + 3*v] != EDGE) map[2*(x + 3*v) + start] += one[j]; if(r == K) { // Teaching King also range move int y = x, n = 0; while(1) { if(board[y+=v] == EDGE) break; if(board[y] != EMPTY) { if(n > 2) map[2*y + start] += one[j]; // outside Lion range break; } n++; } } } else if(r <= L) { // true Lion, also Knight jump if(r < L) { // Lion plus (limited) range int y = x, n = 0; int rg = (r == S ? 36 : 3); while(n++ < rg) { if(board[y+=v] == EDGE) break; if(board[y] != EMPTY) { if(n > 2) map[2*y + start] += one[j]; // outside Lion range break; } } } v = nStep[j]; if(board[x + v] != EMPTY && board[x + v] != EDGE && r != W) map[2*(x + v) + start] += one[8]; } } } } else if(r == C) { // FIDE Pawn diagonal if(board[x + v] != EMPTY && board[x + v] != EDGE) map[2*(x + v) + start] += one[j]; } continue; } while(r-- > 0) { if(board[x+=v] != EMPTY) { mob += dist[x-v-p[i].pos]; if(board[x] != EDGE) map[2*x + start] += one[j], mob += (board[x] ^ start) & 1; #if 1 if(p[i].range[j] > X) { // jump capturer int c = p[i].qval; if(p[board[x]].qval < c) { x += v; // go behind directly captured piece, if jumpable while(p[board[x]].qval < c) { // kludge alert: EDGE has qval = 5, blocking everything if(board[x] != EMPTY) { // int n = map[2*x + start] & attackMask[j]; // map[2*x + start] += (n < 3*one[j] ? 3*one[j] : one[j]); // first jumper gets 2 extra (to ease incremental update) map[2*x + start] += one[j]; // for now use true count } x += v; } } } #endif break; } } } totMob += mob * p[i].mobWeight; } if(!level) printf("# mobility %d = %d\n", start, totMob); return totMob; } void MapFromScratch (int *map) { int i; for(i=0; i<2*bsize; i++) map[i] = 0; mobilityScore = MapOneColor(1, last[WHITE], map); mobilityScore -= MapOneColor(0, last[BLACK], map); } int MakeMove(Move m, UndoInfo *u) { int deferred = ABSENT; // first execute move on board u->from = m>>SQLEN & SQUARE; u->to = m & SQUARE; u->piece = board[u->from]; board[u->from] = EMPTY; u->booty = 0; u->gain = 0; u->loss = 0; u->revMoveCount = cnt50++; u->savKeyL = hashKeyL; u->savKeyH = hashKeyH; u->epVictim[0] = EMPTY; u->saveDelta = promoDelta; u->filling = filling; if(p[u->piece].promoFlag & LAST_RANK) cnt50 = 0; // forward piece: move is irreversible // TODO: put in some test for forward moves of non-backward pieces? // int n = board[promoSuppress-1]; // if( n != EMPTY && (n&TYPE) == xstm && p[n].value == 8 ) NewNonCapt(promoSuppress-1, 16, 0); if(p[u->piece].value == FVAL) { // move with Fire Demon int i, f=~fireFlags[u->piece-2]; for(i=0; i<8; i++) fireBoard[u->from + kStep[i]] &= f; // clear old burn zone } if(m & (PROMOTE | DEFER)) { if(m & DEFER) { // essential deferral: inform caller, but nothing special deferred = u->to; u->new = u->piece; } else { p[u->piece].pos = ABSENT; u->new = p[u->piece].promo; u->booty = p[u->new].value - p[u->piece].value; cnt50 = 0; // promotion irreversible } } else u->new = u->piece; if(u->to >= SPECIAL) { // two-step Lion move if(u->to >= CASTLE) { // move Rook, faking it was an e.p. victim so that UnMake works automatically u->epSquare = epList[u->to - SPECIAL]; u->ep2Square = ep2List[u->to - SPECIAL]; u->epVictim[0] = board[u->epSquare]; // kludgy: fake that King e.p. captured the Rook! u->epVictim[1] = board[u->ep2Square]; // should be EMPTY (but you never know, so save as well). board[u->ep2Square] = u->epVictim[0]; // but put Rook back board[u->epSquare] = EMPTY; p[u->epVictim[0]].pos = u->ep2Square; p[u->epVictim[1]].pos = ABSENT; u->to = toList[u->to - SPECIAL]; hashKeyL ^= p[u->epVictim[0]].pieceKey * squareKey[u->epSquare]; hashKeyH ^= p[u->epVictim[0]].pieceKey * squareKey[u->epSquare+BH]; hashKeyL ^= p[u->epVictim[0]].pieceKey * squareKey[u->ep2Square]; hashKeyH ^= p[u->epVictim[0]].pieceKey * squareKey[u->ep2Square+BH]; } else { // take care of first e.p. victim u->epSquare = u->from + epList[u->to - SPECIAL]; // decode u->epVictim[0] = board[u->epSquare]; // remember for takeback board[u->epSquare] = EMPTY; // and remove p[u->epVictim[0]].pos = ABSENT; // now take care of (optional) second e.p. victim u->ep2Square = u->from + ep2List[u->to - SPECIAL]; // This is the (already evacuated) from-square when there is none! u->epVictim[1] = board[u->ep2Square]; // remember board[u->ep2Square] = EMPTY; // and remove p[u->epVictim[1]].pos = ABSENT; // decode the true to-square, and correct difEval and hash key for the e.p. captures u->to = u->from + toList[u->to - SPECIAL]; u->booty += p[u->epVictim[1]].value + PST[p[u->epVictim[1]].pst + u->ep2Square]; u->booty += p[u->epVictim[0]].value + PST[p[u->epVictim[0]].pst + u->epSquare]; u->gain += p[u->epVictim[1]].value; u->gain += p[u->epVictim[0]].value; promoDelta += p[u->epVictim[0]].promoGain; promoDelta += p[u->epVictim[1]].promoGain; filling -= p[u->epVictim[0]].bulk; filling -= p[u->epVictim[1]].bulk; hashKeyL ^= p[u->epVictim[0]].pieceKey * squareKey[u->epSquare]; hashKeyH ^= p[u->epVictim[0]].pieceKey * squareKey[u->epSquare+BH]; hashKeyL ^= p[u->epVictim[1]].pieceKey * squareKey[u->ep2Square]; hashKeyH ^= p[u->epVictim[1]].pieceKey * squareKey[u->ep2Square+BH]; if(p[u->piece].value != LVAL && p[u->epVictim[0]].value == LVAL) deferred |= PROMOTE; // flag non-Lion x Lion cnt50 = 0; // double capture irreversible } } if(u->fireMask & fireBoard[u->to]) { // we moved next to enemy Fire Demon (must be done after SPECIAL, to decode to-sqr) p[u->piece].pos = ABSENT; // this is suicide: implement as promotion to EMPTY u->new = EMPTY; u->booty -= p[u->piece].value; cnt50 = 0; } else if(p[u->piece].value == FVAL) { // move with Fire Demon that survives: burn int i, f=fireFlags[u->piece-2]; for(i=0; i<8; i++) { int x = u->to + kStep[i], burnVictim = board[x]; fireBoard[x] |= f; // mark new burn zone u->epVictim[i+1] = burnVictim; // remember all neighbors, just in case if(burnVictim != EMPTY && (burnVictim & TYPE) == xstm) { // opponent => actual burn board[x] = EMPTY; // remove it p[burnVictim].pos = ABSENT; u->booty += p[burnVictim].value + PST[p[burnVictim].pst + x]; u->gain += p[burnVictim].value; promoDelta += p[burnVictim].promoGain; filling -= p[burnVictim].bulk; hashKeyL ^= p[burnVictim].pieceKey * squareKey[x]; hashKeyH ^= p[burnVictim].pieceKey * squareKey[x + BH]; cnt50 = 0; // actually burning something makes the move irreversible } } u->epVictim[0] = EDGE; // kludge to flag to UnMake this is special move } u->victim = board[u->to]; p[u->victim].pos = ABSENT; if(p[u->victim].ranking >= 5 && p[u->piece].ranking < 4) { // contageous piece captured by non-royal u->booty -= p[u->new].value; u->new = u->piece & 1 | (u->victim - 2 & ~3) + 2; // promote to it (assumes they head the list in pairs) if(p[u->new].pos != ABSENT) u->new += 2; p[u->piece].pos = ABSENT; u->booty += p[u->new].value; } u->booty += PST[p[u->new].pst + u->to] - PST[p[u->piece].pst + u->from]; filling += p[u->new].bulk - p[u->piece].bulk - p[u->victim].bulk; promoDelta += p[u->new].promoGain - p[u->piece].promoGain + p[u->victim].promoGain; u->booty += p[u->victim].value + PST[p[u->victim].pst + u->to]; u->gain += p[u->victim].value; if(u->victim != EMPTY) { cnt50 = 0; // capture irreversible if(attacks[2*u->to + xstm]) u->loss = p[u->piece].value; // protected } p[u->new].pos = u->to; board[u->to] = u->new; promoDelta = -promoDelta; hashKeyL ^= p[u->new].pieceKey * squareKey[u->to] ^ p[u->piece].pieceKey * squareKey[u->from] ^ p[u->victim].pieceKey * squareKey[u->to]; hashKeyH ^= p[u->new].pieceKey * squareKey[u->to+BH] ^ p[u->piece].pieceKey * squareKey[u->from+BH] ^ p[u->victim].pieceKey * squareKey[u->to+BH]; return deferred; } void UnMake(UndoInfo *u) { if(u->epVictim[0]) { // move with side effects if(u->epVictim[0] == EDGE) { // fire-demon burn int i, f=~fireFlags[u->piece-2]; for(i=0; i<8; i++) { int x = u->to + kStep[i]; fireBoard[x] &= f; board[x] = u->epVictim[i+1]; p[board[x]].pos = x; // even EDGE should have dummy entry in piece list } } else { // put Lion victim of first leg back p[u->epVictim[1]].pos = u->ep2Square; board[u->ep2Square] = u->epVictim[1]; p[u->epVictim[0]].pos = u->epSquare; board[u->epSquare] = u->epVictim[0]; } } if(p[u->piece].value == FVAL) { int i, f=fireFlags[u->piece-2]; for(i=0; i<8; i++) fireBoard[u->from + kStep[i]] |= f; // restore old burn zone } p[u->victim].pos = u->to; board[u->to] = u->victim; // can be EMPTY p[u->new].pos = ABSENT; p[u->piece].pos = u->from; // this can be the same as above board[u->from] = u->piece; cnt50 = u->revMoveCount; hashKeyL = u->savKeyL; hashKeyH = u->savKeyH; filling = u->filling; promoDelta = u->saveDelta; } void GenCapts (int sqr, int victimValue) { // generate all moves that capture the piece on the given square int i, att = attacks[2*sqr + stm]; //printf("GenCapts(%c%d,%d) %08x\n",sqr%BW+'a',sqr/BW,victimValue,att); if(!att) return; // no attackers at all! for(i=0; i<8; i++) { // try all rays int x, v, jcapt=0; if(att & attackMask[i]) { // attacked by move in this direction v = -kStep[i]; x = sqr; while( board[x+=v] == EMPTY ); // scan towards source until we encounter a 'stop' //printf("stop @ %c%d (dir %d)\n",x%BW+'a',x/BW,i); if((board[x] & TYPE) == stm) { // stop is ours static int minRange[20] = { 3, 0, 0, 0, 2, 2, 2 }; // K, T, D, L, W, F, S static int maxRange[20] = { 36, 0, 0, 0, 3, 3, 36 }; // K, T, D, L, W, F, S int attacker = board[x], d = dist[x-sqr], r = p[attacker].range[i]; //printf("attacker %d, range %d, dist %d\n", attacker, r, d); if(r >= d || r <= K && d <= maxRange[K-r] && d > minRange[K-r]) { // it has a plain move in our direction that hits us NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; if(!(att & attackMask[i])) continue; // no more; next direction jcapt = p[board[x]].qval; // jump-capturer hierarchy while(board[x+=v] == EMPTY);// one attack accounted for, but more to come, so skip to next stop } } // we get here when we are on a piece that does not attack us through a (limited) ranging move, // it can be our own or an enemy with not (enough) range, or which is blocked do { //printf("scan %x-%x (%d) dir=%d d=%d r=%d att=%o jcapt=%d qval=%d\n", sqr, x, board[x], i, dist[x-sqr], p[board[x]].range[i], att, jcapt, p[board[x]].qval); if((board[x] & TYPE) == stm) { // stop is ours int attacker = board[x], d = dist[x-sqr], r = p[attacker].range[i]; if(jcapt < p[attacker].qval) { // it is a range jumper that jumps over the barrier if(p[attacker].range[i] > 1) { // assumes all jump-captures are infinite range NewCapture(x, sqr, p[attacker].promoFlag); att -= one[i]; } //if(board[x] == EDGE) { printf("edge hit %x-%x dir=%d att=%o\n", sqr, x, i, att); continue; } } else if(r < 0) { // stop has non-standard moves switch(p[attacker].range[i]) { // figure out what he can do (including multi-captures, which causes the complexity) case F: // Lion power + 3-step (as in FF) case S: // Lion power + ranging (as in BS) case L: // Lion if(d > 2) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; // now the multi-captures of designated victim together with lower-valued piece if(d == 2) { // primary victim on second ring; look for victims to take in passing if((board[sqr+v] & TYPE) == xstm && board[sqr+v] > board[sqr]) NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); if((i&1) == 0) { // orthogonal: two extra bent paths v = kStep[i-1]; if((board[x+v] & TYPE) == xstm && board[x+v] > board[sqr]) NewCapture(x, SPECIAL + 8*(i-1&7) + (i+1&7) + victimValue - SORTKEY(attacker), p[attacker].promoFlag); v = kStep[i+1]; if((board[x+v] & TYPE) == xstm && board[x+v] > board[sqr]) NewCapture(x, SPECIAL + 8*(i+1&7) + (i-1&7) + victimValue - SORTKEY(attacker), p[attacker].promoFlag); } } else { // primary victim on first ring int j; for(j=0; j<8; j++) { // we can go on in 8 directions after we captured it in passing int v = kStep[j]; if(sqr + v == x || board[sqr+v] == EMPTY) // hit & run; make sure we include igui (attacker is still at x!) NewCapture(x, SPECIAL + 8*i + j + victimValue, p[attacker].promoFlag); else if((board[sqr+v] & TYPE) == xstm && board[sqr+v] > board[sqr]) { // double capture NewCapture(x, SPECIAL + 8*i + j + victimValue, p[attacker].promoFlag); // other victim after primary if(dist[sqr+v-x] == 1) // other victim also on first ring; reverse order is possible NewCapture(x, SPECIAL + reverse[8*i + j] + victimValue, p[attacker].promoFlag); } } } break; case D: // linear Lion move (as in HF, SE) if(d > 2) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; if(d == 2) { // check if we can take intermediate with it if((board[x-v] & TYPE) == xstm && board[x-v] > board[sqr]) NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. } else { // d=1; can move on to second, or move back for igui NewCapture(x, SPECIAL + 8*i + (i^4) + victimValue, p[attacker].promoFlag); // igui if(board[sqr-v] == EMPTY || (board[sqr-v] & TYPE) == xstm && board[sqr-v] > board[sqr]) NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // hit and run } break; case T: // Lion-Dog move (awful!) case K: if(d > 3) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; if(d == 3) { // check if we can take one or two intermediates (with higher piece index) with it if((board[x-v] & TYPE) == xstm && board[x-v] > board[sqr]) { NewCapture(x, SPECIAL + 64 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. first if((board[x-2*v] & TYPE) == xstm && board[x-2*v] > board[sqr]) NewCapture(x, SPECIAL + 80 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. both } else if((board[x-2*v] & TYPE) == xstm && board[x-2*v] > board[sqr]) NewCapture(x, SPECIAL + 72 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. second } else if(d == 2) { // check if we can take intermediate with it if((board[x-v] & TYPE) == xstm && board[x-v] > board[sqr]) { NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. first, stop at 2nd NewCapture(x, SPECIAL + 88 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // shoot 2nd, take 1st if(board[sqr-v] == EMPTY || (board[sqr-v] & TYPE) == xstm && board[sqr-v] > board[sqr]) NewCapture(x, SPECIAL + 80 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st and 2nd } else if(board[sqr-v] == EMPTY || (board[sqr-v] & TYPE) == xstm && board[sqr-v] > board[sqr]) NewCapture(x, SPECIAL + 72 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 2nd } else { // d=1; can move on to second, or move back for igui NewCapture(x, SPECIAL + 8*i + (i^4) + victimValue, p[attacker].promoFlag); // igui if(board[sqr-v] == EMPTY) { // 2nd empty NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st and run to 2nd if(board[sqr-2*v] == EMPTY || (board[sqr-2*v] & TYPE) == xstm && board[sqr-2*v] > board[sqr]) NewCapture(x, SPECIAL + 72 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st, end on 3rd } else if((board[sqr-v] & TYPE) == stm) { // 2nd own if(board[sqr-2*v] == EMPTY || (board[sqr-2*v] & TYPE) == xstm && board[sqr-2*v] > board[sqr]) NewCapture(x, SPECIAL + 72 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st, end on 3rd } else if((board[sqr-v] & TYPE) == xstm && board[sqr-v] > board[sqr]) { NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st, capture and stop at 2nd NewCapture(x, SPECIAL + 88 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // shoot 2nd if(board[sqr-2*v] == EMPTY || (board[sqr-2*v] & TYPE) == xstm && board[sqr-2*v] > board[sqr]) NewCapture(x, SPECIAL + 80 + i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. 1st and 2nd } } break; case J: // plain jump (as in KY, PH) if(d != 2) break; case I: // jump + step (as in Wa TF) if(d > 2) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; break; case W: // jump + locust jump + 3-slide (Werewolf) if(d > 2) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; if(d == 2) { // check if we can take intermediate with it if((board[x-v] & TYPE) == xstm && board[x-v] > board[sqr]) NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // e.p. } else { // d=1; can move on to second if(board[sqr-v] == EMPTY || (board[sqr-v] & TYPE) == xstm && board[sqr-v] > board[sqr]) NewCapture(x, SPECIAL + 9*i + victimValue - SORTKEY(attacker), p[attacker].promoFlag); // hit and run } break; case C: // FIDE Pawn if(d != 1) break; NewCapture(x, sqr + victimValue - SORTKEY(attacker), p[attacker].promoFlag); att -= one[i]; } } //printf("mask[%d] = %o\n", i, att); if((att & attackMask[i]) == 0) break; } // more attacks to come; scan for next stop if(jcapt < p[board[x]].qval) jcapt = p[board[x]].qval; // raise barrier for range jumpers further upstream while(board[x+=v] == EMPTY); // this should never run off-board, if the attack map is not corrupted } while(1); } } // off-ray attacks if(att & 0700000000) { // Knight attack for(i=0; i<8; i++) { // scan all knight jumps to locate source int x = sqr - nStep[i], attacker = board[x]; if(attacker == EMPTY || (attacker & TYPE) != stm) continue; if(p[attacker].range[i] == L || p[attacker].range[i] < W && p[attacker].range[i] >= S || p[attacker].range[i] == N) { // has Knight jump in our direction NewCapture(x, sqr + victimValue, p[attacker].promoFlag); // plain jump (as in N) if(p[attacker].range[i] < N) { // Lion power; generate double captures over two possible intermediates int v = kStep[i]; // leftish path if((board[x+v] & TYPE) == xstm && board[x+v] > board[sqr]) NewCapture(x, SPECIAL + 8*i + (i+1&7) + victimValue, p[attacker].promoFlag); v = kStep[i+1]; // rightish path if((board[x+v] & TYPE) == xstm && board[x+v] > board[sqr]) NewCapture(x, SPECIAL + 8*(i+1&7) + i + victimValue, p[attacker].promoFlag); } } } } } int Guard (int sqr) { int piece = board[sqr], val; if(piece == EDGE) return 0; val = p[piece].value; if(val == 201) return 3; // Elephant if(val == 152) return 2; // Tiger if(val == 151) return 1; // Gold return 0; } int Fortress (int forward, int king, int lion) { // penalty for lack of Lion-proof fortress int rank = PST[king], anchor, r, l, q, res = 0; if(rank != 2) return 25*(rank-2); anchor = king + forward*(rank-1); q = Guard(anchor); l = Guard(anchor-1); r = Guard(anchor+1); if(!q) return l > 1 || r > 1 ? 0 : -25; if(q == 1) res = 40*(l > 1 && r > 1); // TGT, EGT or TGE get 40 else { // T or E in front of King if(l > 1) res = 30 + (r > 1)*(20 + 5*(q==2)); // TT., ET. or TE. (30), TET (50), TTE (55) else if(r > 1) res = 30; // .TT, .ET or .TE (30) } q = 0; if(filling > 32) { if(r > 1 && Guard(king+2) == 1) q += 10; if(l > 1 && Guard(king-2) == 1) q += 10; q += 5*(Guard(king+1) == 1); q += 5*(Guard(king-1) == 1); if(filling < 96) q = q*(filling - 32)>>6; } return res + q; if(Guard(anchor) == 3 || Guard(anchor+1) == 3 || Guard(anchor-1) == 3) return 0; if(rank == 2 && Guard(king+1) == 3 || Guard(king-1) == 3) return -50; if(Guard(r=anchor) == 2 || Guard(r=anchor+1) == 2 || Guard(r=anchor-1) == 2) return -100 + 50*(Guard(r + forward + 1) == 3 || Guard(r + forward - 1) == 3); return -300; for(r=anchor+1; Guard(r) > 1; r++); for(l=anchor-1; Guard(l) > 1; l--); //if(PATH) printf("# l=%d r=%d\n", l, r); if(Guard(anchor) < 2) { if(r - anchor > anchor - l || // largest group, or if equal, group that contains elephant r - anchor == anchor - l && Guard(r-1) == 3) l = anchor; else r = anchor; } switch(r - l) { case 1: q = 15; break; // no shelter at all, maximum penalty case 2: if(Guard(l+1) == 3) q = 10; // single Elephant offers some shelter else if(Guard(l+forward) == 3 || Guard(l+forward+2) == 3) q = 8; // better if Tiger diagonally in front of it else q = 14; // singe tiger almost no help; break; case 3: q = 5 - (Guard(l+1) == 3 || Guard(l+3) == 3); break; // pair is better if it contains Elephant case 4: q = (Guard(l+2) != 3); // 3 wide: perfect, or nearly so if Elephant not in middle default: ; } //if(PATH) printf("# fortress %d: q=%d l=%d r=%d\n", anchor, q, l, r); return (dist[lion - king] - 23)*q; // reduce by ~half if Lion very far away } int Surround (int stm, int king, int start, int max) { int i, s=0; for(i=start; i<9; i++) { int v, piece, sq = king + neighbors[i]; if((piece = board[sq]) == EDGE || !piece || piece&1^stm) continue; if(p[piece].promoGain) continue; v = p[piece].value; s += -(v > 70) & v; } return (s > max ? max : s); } int Ftest (int side) { int lion = ABSENT, king; if(p[side+2].value == LVAL) lion = p[side+2].pos; if(lion == ABSENT && p[side+4].value == LVAL) lion = p[side+4].pos; king = p[royal[1-side]].pos; if(king == ABSENT) king = p[royal[1-side]+1].pos; return lion == ABSENT ? 0 : Fortress(side ? -BW : BW, king, lion); } int Evaluate (int difEval) { int wLion = ABSENT, bLion = ABSENT, wKing, bKing, score=mobilityScore, f, i, j, max=512; if(tsume) return difEval; if(p[WHITE+2].value == LVAL) wLion = p[WHITE+2].pos; if(p[BLACK+2].value == LVAL) bLion = p[BLACK+2].pos; if(wLion == ABSENT && p[WHITE+4].value == LVAL) wLion = p[WHITE+4].pos; if(bLion == ABSENT && p[BLACK+4].value == LVAL) bLion = p[BLACK+4].pos; #ifdef LIONTRAP # define lionTrap (PST + PST_TRAP) // penalty for Lion in enemy corner, when enemy Lion is nearby if(wLion != ABSENT && bLion != ABSENT) { // both have a Lion static int distFac[36] = { 0, 0, 10, 9, 8, 7, 5, 3, 1 }; score -= ( (1+9*!attacks[2*wLion+WHITE]) * lionTrap[BW*(BH-1)+BH-1-wLion] - (1+9*!attacks[2*bLion+BLACK]) * lionTrap[bLion] ) * distFac[dist[wLion - bLion]]; } # ifdef WINGS // bonus if corner lances are protected by Lion-proof setup (FL + C/S) if(bLion != ABSENT) { if((p[board[BW+lr]].value == 320 || p[board[BW+lr]].value == 220) && p[board[ll+1]].value == 150 && p[board[ll+BW+2]].value == 100) score += 20 + 20*!p[board[ll]].range[2]; if((p[board[BW+lr]].value == 320 || p[board[BW+lr]].value == 220) && p[board[lr-1]].value == 150 && p[board[lr+BW-2]].value == 100) score += 20 + 20*!p[board[lr]].range[2]; } if(wLion != ABSENT) { if((p[board[ul-BW]].value == 320 || p[board[ul-BW]].value == 220) && p[board[ul+1]].value == 150 && p[board[ul-BW+2]].value == 100) score -= 20 + 20*!p[board[ul]].range[2]; if((p[board[ur-BW]].value == 320 || p[board[ur-BW]].value == 220) && p[board[ur-1]].value == 150 && p[board[ur-BW-2]].value == 100) score -= 20 + 20*!p[board[ur]].range[2]; } # endif #endif #ifdef KINGSAFETY // basic centralization in end-game (also facilitates bare-King mating) wKing = p[royal[WHITE]].pos; if(wKing == ABSENT) wKing = p[royal[WHITE]+2].pos; bKing = p[royal[BLACK]].pos; if(bKing == ABSENT) bKing = p[royal[BLACK]+2].pos; if(filling < 32) { int lead = (stm == WHITE ? difEval : -difEval); score += (PST[PST_CENTER+wKing] - PST[PST_CENTER+bKing])*(32 - filling) >> 7; if(lead > 100) score -= PST[PST_CENTER+bKing]*(32 - filling) >> 3; // white leads, drive black K to corner if(lead < -100) score += PST[PST_CENTER+wKing]*(32 - filling) >> 3; // black leads, drive white K to corner max = 16*filling; } # ifdef FORTRESS f = 0; if(bLion != ABSENT) f += Fortress( BW, wKing, bLion); if(wLion != ABSENT) f -= Fortress(-BW, bKing, wLion); score += (filling < 192 ? f : f*(224 - filling) >> 5); // build up slowly # endif # ifdef KSHIELD score += Surround(WHITE, wKing, 1, max) - Surround(BLACK, bKing, 1, max) >> 3; # endif #endif #if KYLIN // bonus for having Kylin in end-game, where it could promote to Lion // depends on board population, defenders around zone entry and proximity to zone if(filling < 128) { int sq; if((wLion = kylin[WHITE]) && (sq = p[wLion].pos) != ABSENT) { int anchor = sq - PST[5*BW*BH - 1 - sq]; // FIXME: PST_ZONDIST indexed backwards score += (512 - Surround(BLACK, anchor, 0, 512))*(128 - filling)*PST[p[wLion].pst + sq] >> 15; } if((bLion = kylin[BLACK]) && (sq = p[bLion].pos) != ABSENT) { int anchor = sq + PST[PST_ZONDIST + sq]; score -= (512 - Surround(WHITE, anchor, 0, 512))*(128 - filling)*PST[p[bLion].pst + sq] >> 15; } } #endif #ifdef PAWNBLOCK // penalty for blocking own P or GB: 20 by slider, 10 by other, but 50 if only RETRACT mode is straight back for(i=last[WHITE]; i > 1 && p[i].value<=50; i-=2) { if((f = p[i].pos) != ABSENT) { // P present, if((j = board[f + BW])&1) // square before it white (odd) piece score -= 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[3] || p[j].range[5] || p[j].value==50); if((j = board[f - BW])&1) // square behind it white (odd) piece score += 7*(p[j].promoGain == 0 & p[j].value<=151); } } for(i=last[BLACK]; i > 1 && p[i].value<=50; i-=2) { if((f = p[i].pos) != ABSENT) { // P present, if((j = board[f - BW]) && !(j&1)) // square before non-empty and even (black) score += 10 + 10*(p[j].promoGain > 0) + 30*!(p[j].range[1] || p[j].range[7] || p[j].value==50); if((j = board[f + BW]) && !(j&1)) // square behind non-empty and even (black) score -= 7*(p[j].promoGain == 0 & p[j].value<=151); } } #endif #ifdef TANDEM if(zone > 0) { int rw = BW*(BH-1-zone), rb = BW*zone, h=0; for(f=0; f 0) h += (p[board[rw+f-BW-1]].pst == PST_ADVANCE); if(f+1 < BH) h += (p[board[rw+f-BW+1]].pst == PST_ADVANCE); } if(p[board[rb+f]].pst == PST_ADVANCE) { h -= (p[board[rb+f+BW]].pst == PST_RETRACT); if(f > 0) h -= (p[board[rb+f+BW-1]].pst == PST_RETRACT); if(f+1 < BH) h -= (p[board[rb+f+BW+1]].pst == PST_RETRACT); } } score += h*TANDEM; } #endif return difEval - (filling*promoDelta >> 8) + (stm ? score : -score); } static inline void FireSet (UndoInfo *tb) { // set fireFlags acording to remaining presene of Fire Demons int i; for(i=stm+2; p[i].value == FVAL; i++) // Fire Demons are always leading pieces in list if(p[i].pos != ABSENT) tb->fireMask |= fireFlags[i-2]; } void TerminationCheck(); #define QSdepth 4 int Search (int alpha, int beta, int difEval, int depth, int lmr, int oldPromo, int promoSuppress, int threshold) { int i, j, k, phase, king, nextVictim, defer, autoFail=0, inCheck = 0, late=100000, ep; int firstMove, oldMSP = msp, curMove, sorted, bestMoveNr; int resDep, iterDep, ext; int myPV = pvPtr; int score, bestScore, oldBest, curEval, iterAlpha; Move move, nullMove; UndoInfo tb; #ifdef HASH Move hashMove; int index, nr, hit; #endif if(PATH) /*pboard(board),pmap(attacks, BLACK),*/printf("search(%d) {%d,%d} eval=%d, stm=%d (flag=%d)\n",depth,alpha,beta,difEval,stm,abortFlag),fflush(stdout); xstm = stm ^ WHITE; //printf("map made\n");fflush(stdout); // in-check test and TSUME filter { k = p[king=royal[stm]].pos; if( k == ABSENT) { if((k = p[king + 2].pos) == ABSENT && (!tsume || tsume & stm+1)) return -INF; // lose when no King (in tsume only for side to be mated) } else if(p[king + 2].pos != ABSENT) { if(tsume && tsume & stm+1) { retDep = 60; return INF; // we win when not in check } k = ABSENT; // two kings is no king... } if( k != ABSENT) { // check is possible if(!attacks[2*k + xstm]) { if(tsume && tsume & stm+1) { retDep = 60; return INF; // we win when not in check } } #ifdef CHECKEXT else { inCheck = 1; if(depth >= QSdepth) depth++; } #endif } } if(!level) {for(i=0; i<5; i++)printf("# %d %08x, %d\n", i, repStack[200-i], checkStack[200-i]);} // KING CAPTURE k = p[king=royal[xstm]].pos; if( k != ABSENT) { if(attacks[2*k + stm]) { if( p[king + 2].pos == ABSENT ) return INF; // we have an attack on his only King } } else { // he has no king! Test for attacks on Crown Prince k = p[king + 2].pos; if(k == ABSENT ? !tsume : attacks[2*k + stm]) return INF; // we have attack on Crown Prince } //printf("King safe\n");fflush(stdout); // EVALUATION & WINDOW SHIFT curEval = Evaluate(difEval) -20*inCheck; alpha -= (alpha < curEval); beta -= (beta <= curEval); if(!(nodes++ & 4095)) TerminationCheck(); pv[pvPtr++] = 0; // start empty PV, directly behind PV of parent if(inCheck) lmr = 0; else depth -= lmr; // no LMR of checking moves firstMove = curMove = sorted = msp += 50; // leave 50 empty slots in front of move list iterDep = -(depth == 0); tb.fireMask = phase = 0; #ifdef HASH index = hashKeyL ^ 327*stm ^ (oldPromo + 987981)*(63121 + promoSuppress); index = index + (index >> 16) & hashMask; nr = (hashKeyL >> 30) & 3; hit = -1; if(hashTable[index].lock[nr] == hashKeyH) hit = nr; else if(hashTable[index].lock[4] == hashKeyH) hit = 4; if(PATH) printf("# probe hash index=%x hit=%d\n", index, hit),fflush(stdout); if(hit >= 0) { bestScore = hashTable[index].score[hit]; hashMove = hashTable[index].move[hit]; if((bestScore <= alpha || hashTable[index].flag[hit] & H_LOWER) && (bestScore >= beta || hashTable[index].flag[hit] & H_UPPER) ) { iterDep = resDep = hashTable[index].depth[hit]; bestMoveNr = 0; if(!level) iterDep = 0; // no hash cutoff in root if(lmr && bestScore <= alpha && iterDep == depth) depth ++, lmr--; // self-deepening LMR if(pvCuts && iterDep >= depth && hashMove && bestScore < beta && bestScore > alpha) iterDep = depth - 1; // prevent hash cut in PV node } } else { // decide on replacement if(depth >= hashTable[index].depth[nr] || depth+1 == hashTable[index].depth[nr] && !(nodes&3)) hit = nr; else hit = 4; hashMove = 0; } if(PATH) printf("# iterDep = %d score = %d move = %s\n",iterDep,bestScore,MoveToText(hashMove,0)),fflush(stdout); #endif if(depth > QSdepth && iterDep < QSdepth) iterDep = QSdepth; // full-width: start at least from 1-ply if(PATH)printf("iterDep=%d\n", iterDep); while(++iterDep <= depth) { if(flag && depth>= 0) printf("iter %d:%d\n", depth,iterDep),fflush(stdout); oldBest = bestScore; iterAlpha = alpha; bestScore = -INF; bestMoveNr = 0; resDep = 60; if(PATH)printf("new iter %d\n", iterDep); if(depth <= QSdepth) { bestScore = curEval; resDep = QSdepth; if(bestScore > alpha) { alpha = bestScore; if(PATH)printf("stand pat %d (beta=%d)\n", bestScore, beta); if(bestScore >= beta) goto cutoff; } } for(curMove = firstMove; ; curMove++) { // loop over moves if(flag && depth>= 0) printf("phase=%d: first/curr/last = %d / %d / %d\n", phase, firstMove, curMove, msp);fflush(stdout); // MOVE SOURCE if(curMove >= msp) { // we ran out of moves; generate some new if(PATH)printf("new moves, phase=%d\n", phase),fflush(stdout); switch(phase) { case 0: // null move #ifdef NULLMOVE if(depth > QSdepth && curEval >= beta && !inCheck && filling > 10) { int nullDep = depth - 3; stm ^= WHITE; path[level++] = 0; if(PATH) printf("%d:%d null move\n", level, depth),fflush(stdout); score = -Search(-beta, 1-beta, -difEval, nullDep= beta) { msp = oldMSP; retDep += 3; pvPtr = myPV; return score + (score < curEval); } // else depth += lmr, lmr = 0; } #endif if(tenFlag) FireSet(&tb); // in tenjiku we must identify opposing Fire Demons to perform any moves //if(PATH) printf("mask=%x\n",tb.fireMask),pbytes(fireBoard); phase = 1; case 1: // hash move phase = 2; #ifdef HASH if(hashMove && (depth > QSdepth || // must be capture in QS (hashMove & SQUARE) >= SPECIAL || board[hashMove & SQUARE] != EMPTY)) { moveStack[sorted = msp++] = hashMove; goto extractMove; } #endif case 2: // capture-gen init nextVictim = xstm; autoFail = (depth == 0); phase = 3; case 3: // generate captures if(PATH) printf("%d:%2d:%2d next victim %d/%d\n",level,depth,iterDep,curMove,msp),fflush(stdout); while(nextVictim < last[xstm]) { // more victims exist int group, to = p[nextVictim += 2].pos; // take next if(to == ABSENT) continue; // ignore if absent if(!attacks[2*to + stm]) continue; // skip if not attacked group = p[nextVictim].value; // remember value of this found victim if(iterDep <= QSdepth + 1 && 2*group + curEval + 30 < alpha) { resDep = QSdepth + 1; nextVictim -= 2; if(bestScore < 2*group + curEval + 30) bestScore = 2*group + curEval + 30; goto cutoff; } if(PATH) printf("%d:%2d:%2d group=%d, to=%c%d\n",level,depth,iterDep,group,to%BW+'a',to/BW+ONE),fflush(stdout); GenCapts(to, 0); if(PATH) printf("%d:%2d:%2d first=%d msp=%d\n",level,depth,iterDep,firstMove,msp),fflush(stdout); while(nextVictim < last[xstm] && p[nextVictim+2].value == group) { // more victims of same value exist to = p[nextVictim += 2].pos; // take next if(to == ABSENT) continue; // ignore if absent if(!attacks[2*to + stm]) continue; // skip if not attacked if(PATH) printf("%d:%2d:%2d p=%d, to=%c%d\n",level,depth,iterDep,nextVictim,to%BW+'a',to/BW+ONE),fflush(stdout); GenCapts(to, 0); if(PATH) printf("%d:%2d:%2d msp=%d\n",level,depth,iterDep,msp),fflush(stdout); } if(PATH) printf("captures on %d generated, msp=%d, group=%d, threshold=%d\n", nextVictim, msp, group, threshold),fflush(stdout); goto extractMove; // in auto-fail phase, only search if they might auto-fail-hi } if(PATH) printf("# autofail=%d\n", autoFail); if(autoFail) { // non-captures cannot auto-fail; flush queued captures first if(PATH) printf("# autofail end (%d-%d)\n", firstMove, msp); autoFail = 0; curMove = firstMove - 1; continue; // release stashed moves for search } phase = 4; // out of victims: all captures generated if(chessFlag && (ep = promoSuppress & SQUARE) != ABSENT) { // e.p. rights. Create e.p. captures as Lion moves int n = board[ep-1], old = msp; // a-side neighbor of pushed pawn if( n != EMPTY && (n&TYPE) == stm && p[n].value == pVal ) NewCapture(ep-1, SPECIAL + 20 - 4*stm, 0); n = board[ep+1]; // h-side neighbor of pushed pawn if( n != EMPTY && (n&TYPE) == stm && p[n].value == pVal ) NewCapture(ep+1, SPECIAL + 52 - 4*stm, 0); if(msp != old) goto extractMove; // one or more e.p. capture were generated } case 4: // dubious captures #if 0 while( dubious < framePtr + 250 ) // add dubious captures back to move stack moveStack[msp++] = moveStack[dubious++]; if(curMove != msp) break; #endif phase = 5; case 5: // killers if(depth <= QSdepth) { if(resDep > QSdepth) resDep = QSdepth; goto cutoff; } phase = 6; case 6: // non-captures nonCapts = msp; nullMove = GenNonCapts(oldPromo); if(msp == nonCapts) goto cutoff; #ifdef KILLERS { // swap killers to front Move h = killer[level][0]; int j = curMove; for(i=curMove; i= 0 || (PATH)) printf("%2d:%d extract %d/%d\n", depth, iterDep, curMove, msp); if(curMove > sorted) { move = moveStack[sorted=j=curMove]; for(i=curMove+1; i move) move = moveStack[j=i]; // search move with highest priority moveStack[j] = moveStack[curMove]; moveStack[curMove] = move; // swap highest-priority move to front of remaining if(move == 0) { msp = curMove--; continue; } // all remaining moves must be 0; clip off move list } else { move = moveStack[curMove]; if(move == 0) continue; // skip invalidated move } if(flag & depth >= 0) printf("%2d:%d found %d/%d %08x %s\n", depth, iterDep, curMove, msp, moveStack[curMove], MoveToText(moveStack[curMove], 0)); // RECURSION stm ^= WHITE; defer = MakeMove(move, &tb); ext = (depth == 0); // when out of depth we extend captures if there was no auto-fail-hi // if(level == 1 && randomize) tb.booty += (hashKeyH * seed >> 24 & 31) - 20; if(autoFail) { UnMake(&tb); // never search moves during auto-fail phase xstm = stm; stm ^= WHITE; if(tb.gain <= threshold) { // we got to moves that cannot possibly auto-fail autoFail = 0; curMove = firstMove-1; continue; // release all for search } if(tb.gain - tb.loss > threshold) { bestScore = INF+1; resDep = 0; goto leave; // auto-fail-hi } else continue; // ignore for now if not obvious refutation } if(flag & depth >= 0) printf("%2d:%d made %d/%d %s\n", depth, iterDep, curMove, msp, MoveToText(moveStack[curMove], 0)); for(i=2; i<=cnt50; i+=2) if(repStack[level-i+200] == hashKeyH) { retDep = iterDep; if(repDraws) { score = 0; goto repetition; } if(!allowRep) { moveStack[curMove] = 0; // erase forbidden move if(!level) repeatMove[repCnt++] = move & 0xFFFFFF; // remember outlawed move } else { // check for perpetuals // int repKey = 1; // for(i-=level; i>1; i-=2) {repKey &= checkStack[200-i]; if(!level)printf("# repkey[%d] = %d\n", 200-i, repKey);} if(inCheck) { score = INF-20; goto repetition; } // we might be subject to perpetual check: score as win if(i == 2 && repStack[level+199] == hashKeyH) { score = INF-20; goto repetition; } // consecutive passing } score = -INF + 8*allowRep; goto repetition; } repStack[level+200] = hashKeyH; if(PATH) printf("%d:%2d:%d %3d %6x %-10s %6d %6d\n", level, depth, iterDep, curMove, moveStack[curMove], MoveToText(moveStack[curMove], 0), score, bestScore),fflush(stdout); path[level++] = move; attacks += 2*bsize; MapFromScratch(attacks); // for as long as incremental update does not work. //if(flag & depth >= 0) printf("%2d:%d mapped %d/%d %s\n", depth, iterDep, curMove, msp, MoveToText(moveStack[curMove], 0)); //if(PATH) pmap(attacks, stm); if(chuFlag && (p[tb.victim].value == LVAL || p[tb.epVictim[0]].value == LVAL)) {// verify legality of Lion capture in Chu Shogi score = 0; if(p[tb.piece].value == LVAL) { // Ln x Ln: can make Ln 'vulnerable' (if distant and not through intemediate > GB) if(dist[tb.from-tb.to] != 1 && attacks[2*tb.to + stm] && p[tb.epVictim[0]].value <= 50) score = -INF; // our Lion is indeed made vulnerable and can be recaptured } else { // other x Ln if(promoSuppress & PROMOTE) { // non-Lion captures Lion after opponent did same if(!okazaki || attacks[2*tb.to + stm]) score = -INF; } defer |= PROMOTE; // if we started, flag he cannot do it in reply } if(score == -INF) { if(level == 1) repeatMove[repCnt++] = move & 0xFFFFFF | (p[tb.piece].value == LVAL ? 3<<24 : 1 << 24); moveStack[curMove] = 0; // zap illegal moves goto abortMove; } } #if 1 score = -Search(-beta, -iterAlpha, -difEval - tb.booty, iterDep-1+ext, curMove >= late && iterDep > QSdepth + LMR, promoSuppress & ~PROMOTE, defer, depth ? INF : tb.gain); #else score = 0; #endif abortMove: attacks -= 2*bsize; level--; repetition: UnMake(&tb); xstm = stm; stm ^= WHITE; if(abortFlag > 0) { // unwind search printf("# abort (%d) @ %d\n", abortFlag, level); if(curMove == firstMove) bestScore = oldBest, bestMoveNr = firstMove; // none searched yet goto leave; } #if 1 if(PATH) printf("%d:%2d:%d %3d %6x %-10s %6d %6d (%d)\n", level, depth, iterDep, curMove, moveStack[curMove], MoveToText(moveStack[curMove], 0), score, bestScore, GetTickCount()),fflush(stdout); // ALPHA-BETA STUFF if(score > bestScore) { bestScore = score; bestMoveNr = curMove; if(score > iterAlpha) { iterAlpha = score; if(curMove < firstMove + 5) { // if not too much work, sort move to front int i; for(i=curMove; i>firstMove; i--) { moveStack[i] = moveStack[i-1]; } moveStack[firstMove] = move; } else { // late move appended in front of list, and leaves hole in list moveStack[--firstMove] = move; moveStack[curMove] = 0; } bestMoveNr = firstMove; if(score >= beta) { // beta cutoff #ifdef KILLERS if(iterDep == depth && move != killer[level][0] && (tb.victim == EMPTY && (move & SQUARE) < SPECIAL)) { // update killer killer[level][1] = killer[level][0]; killer[level][0] = move; } #endif resDep = retDep+1-ext; goto cutoff; } { int i=pvPtr; for(pvPtr = myPV+1; pv[pvPtr++] = pv[i++]; ); // copy daughter PV pv[myPV] = move; // behind our move (pvPtr left at end of copy) } } } if(retDep+1-ext < resDep) resDep = retDep+1-ext; #endif } // next move cutoff: if(!level) { // root node lastRootIter = GetTickCount() - startTime; if(postThinking > 0) { int i; // WB thinking output printf("%d %d %d %d", iterDep-QSdepth, bestScore, lastRootIter/10, nodes); if(ponderMove) printf(" (%s)", MoveToText(ponderMove, 0)); for(i=0; pv[i]; i++) printf(" %s", MoveToText(pv[i], 0)); if(iterDep == QSdepth+1) printf(" { root eval = %4.2f dif = %4.2f; abs = %4.2f f=%d D=%4.2f %d/%d}", curEval/100., difEval/100., PSTest()/100., filling, promoDelta/100., Ftest(0), Ftest(1)); printf("\n"); fflush(stdout); } if(!(abortFlag & 1) && GetTickCount() - startTime > tlim1) break; // do not start iteration we can (most likely) not finish } if(resDep > iterDep) iterDep = resDep; // skip iterations if we got them for free #ifdef LMR if(lmr && bestScore <= alpha && iterDep == depth) depth++, lmr--; // self-deepen on fail-low reply to late move by lowering reduction #endif if(stalemate && bestScore == -INF && !inCheck) bestScore = 0; // stalemate #ifdef HASH // hash store hashTable[index].lock[hit] = hashKeyH; hashTable[index].depth[hit] = resDep; hashTable[index].score[hit] = bestScore; hashTable[index].flag[hit] = (bestScore < beta) * H_UPPER; if(bestScore > alpha) { hashTable[index].flag[hit] |= H_LOWER; hashTable[index].move[hit] = bestMoveNr ? moveStack[bestMoveNr] : 0; } else hashTable[index].move[hit] = 0; #endif } // next depth leave: retMSP = msp; retFirst = firstMove; msp = oldMSP; // pop move list pvPtr = myPV; // pop PV retMove = bestMoveNr ? moveStack[bestMoveNr] : 0; retDep = resDep - (inCheck & depth >= QSdepth) + lmr; if(PATH) printf("return %d: %d %d (t=%d s=%d lim=%d)\n", depth, bestScore, curEval, GetTickCount(), startTime, tlim1),fflush(stdout); return bestScore + (bestScore < curEval); } void pplist() { int i, j; for(i=0; i<182; i++) { printf("%3d. %4d %3d %4d %02x %d %2d %x %3d %4d ", i, p[i].value, p[i].promo, p[i].pos, p[i].promoFlag&255, p[i].mobWeight, p[i].qval, p[i].bulk, p[i].promoGain, p[i].pst); for(j=0; j<8; j++) printf(" %3d", p[i].range[j]); if(i<2 || i>11) printf("\n"); else printf(" %02x %d\n", fireFlags[i-2]&255, p[i].ranking); } printf("last: %d / %d\nroyal %d / %d\n", last[WHITE], last[BLACK], royal[WHITE], royal[BLACK]); } void pboard (int *b) { int i, j; for(i=BH+2; i>-4; i--) { printf("#"); for(j=-3; j=0; i--) { for(j=0; j=0; i--) { printf("#"); for(j=0; j>SQLEN & SQUARE; t = m & SQUARE; printf("# %3d. %08x %3d-%3d %s\n", i, m, f, t, MoveToText(m, 0)); } } /********************************************************/ /* Example of a WinBoard-protocol driver, by H.G.Muller */ /********************************************************/ #include // four different constants, with values for WHITE and BLACK that suit your engine #define NONE 3 #define ANALYZE 4 // some value that cannot occur as a valid move #define INVALID 0 // some parameter of your engine #define MAXMOVES 2000 /* maximum game length */ #define MAXPLY 60 /* maximum search depth */ #define OFF 0 #define ON 1 typedef Move MOVE; int moveNr; // part of game state; incremented by MakeMove MOVE gameMove[MAXMOVES]; // holds the game history // Some routines your engine should have to do the various essential things int MakeMove2(int stm, MOVE move); // performs move, and returns new side to move void UnMake2(MOVE move); // unmakes the move; int Setup2(char *fen); // sets up the position from the given FEN, and returns the new side to move void SetMemorySize(int n); // if n is different from last time, resize all tables to make memory usage below n MB char *MoveToText(MOVE move, int m); // converts the move from your internal format to text like e2e2, e1g1, a7a8q. MOVE ParseMove(char *moveText); // converts a long-algebraic text move to your internal move format int SearchBestMove(MOVE *move, MOVE *ponderMove); void PonderUntilInput(int stm); // Search current position for stm, deepening forever until there is input. UndoInfo undoInfo; int sup0, sup1, sup2; // promo suppression squares int lastLift, lastPut; int InCheck () { int k = p[royal[stm]].pos; if( k == ABSENT) k = p[royal[stm] + 2].pos; else if(p[royal[stm] + 2].pos != ABSENT) k = ABSENT; // two kings is no king... if( k != ABSENT) { MapFromScratch(attacks); if(attacks[2*k + 1 - stm]) return 1; } return 0; } int MakeMove2 (int stm, MOVE move) { int i, inCheck = InCheck(); FireSet(&undoInfo); sup0 = sup1; sup1 = sup2; sup2 = MakeMove(move, &undoInfo); if(chuFlag && p[undoInfo.victim].value == LVAL && p[undoInfo.piece].value != LVAL) sup2 |= PROMOTE; rootEval = -rootEval - undoInfo.booty; for(i=0; i<200; i++) repStack[i] = repStack[i+1], checkStack[i] = checkStack[i+1]; repStack[199] = hashKeyH, checkStack[199] = inCheck; printf("# makemove %08x %c%d %c%d\n", move, sup1%BW+'a', sup1/BW, sup2%BW+'a', sup2/BW); return stm ^ WHITE; } void UnMake2 (MOVE move) { int i; rootEval = -rootEval - undoInfo.booty; UnMake(&undoInfo); for(i=200; i>0; i--) repStack[i] = repStack[i-1], checkStack[i] = checkStack[i-1]; sup2 = sup1; sup1 = sup0; } int Setup2 (char *fen) { char *p; int stm = WHITE; if(fen) { char *q = strchr(fen, '\n'); if(q) *q = 0; if(q = strchr(fen, ' ')) stm = (q[1] == 'b' ? BLACK : WHITE); // fen contains color field } else fen = array; rootEval = promoDelta = filling = cnt50 = moveNr = 0; SetUp(fen, currentVariant); sup0 = sup1 = sup2 = ABSENT; hashKeyH = hashKeyL = 87620895*currentVariant + !!fen; for(p=startPos; *p++ = *fen++; ) {} // remember last start position for undo return stm; } void SetMemorySize (int n) { #ifdef HASH static HashBucket *realHash; static intptr_t oldSize; intptr_t l, m = 1; while(m*sizeof(HashBucket) <= n*512UL) m <<= 1; // take largest power-of-2 that fits if(m != oldSize) { if(oldSize) free(realHash); hashMask = m*1024 - 1; oldSize = m; realHash = malloc(m*1024*sizeof(HashBucket) + 64); l = (intptr_t) realHash; hashTable = (HashBucket*) (l & ~63UL); // align with cache line } #endif } char * MoveToText (MOVE move, int multiLine) { static char buf[50]; int f = move>>SQLEN & SQUARE, g = f, t = move & SQUARE; char *promoChar = ""; if(f == t) { sprintf(buf, "@@@@"); return buf; } // null-move notation in WB protocol buf[0] = '\0'; if(t >= SPECIAL) { if(t < CASTLE) { // castling is printed as a single move, implying its side effects int e = f + epList[t - SPECIAL]; if(ep2List[t - SPECIAL]) { int e2 = f + ep2List[t - SPECIAL]; // printf("take %c%d\n", e%BW+'a', e/BW+ONE); sprintf(buf+strlen(buf), "%c%d%c%d,", f%BW+'a', f/BW+ONE, e2%BW+'a', e2/BW+ONE); f = e2; if(multiLine) printf("move %s\n", buf), buf[0] = '\0'; } // printf("take %c%d\n", e%BW+'a', e/BW+ONE); sprintf(buf, "%c%d%c%d,", f%BW+'a', f/BW+ONE, e%BW+'a', e/BW+ONE); f = e; if(multiLine) printf("move %s\n", buf), buf[0] = '\0'; } t = g + toList[t - SPECIAL]; } if(move & PROMOTE) promoChar = currentVariant == V_MAKRUK ? "m" : currentVariant == V_WOLF ? "r" : repDraws ? "q" : "+"; sprintf(buf+strlen(buf), "%c%d%c%d%s", f%BW+'a', f/BW+ONE, t%BW+'a', t/BW+ONE, promoChar); return buf; } int ReadSquare (char *p, int *sqr) { int f, r; f = p[0] - 'a'; r = atoi(p + 1) - ONE; *sqr = r*BW + f; return 2 + (r + ONE > 9); } int listStart, listEnd; char boardCopy[BSIZE]; void ListMoves () { // create move list on move stack int i; for(i=0; i< BSIZE; i++) boardCopy[i] = !!board[i]; MapFromScratch(attacks); postThinking--; repCnt = 0; tlim1 = tlim2 = tlim3 = 1e8; abortFlag = msp = 0; Search(-INF-1, INF+1, 0, QSdepth+1, 0, sup1 & ~PROMOTE, sup2, INF); postThinking++; listStart = retFirst; msp = retMSP; if(currentVariant == V_LION) GenCastlings(); listEnd = msp; for(i=listStart; i> SQLEN & SQUARE; if(to >= SPECIAL) continue; if(p[board[to]].ranking >= 5 && p[board[from]].ranking < 4) moveStack[i] |= PROMOTE; } } MOVE ParseMove (char *moveText) { int i, j, f, t, t2, e, ret, deferred=0; char c = moveText[0]; moveText += ReadSquare(moveText, &f); moveText += ReadSquare(moveText, &t); t2 = t; if(*moveText == ',') { moveText++; moveText += ReadSquare(moveText, &e); if(e != t) return INVALID; // must continue with same piece e = t; moveText += ReadSquare(moveText, &t); for(i=0; i<8; i++) if(f + kStep[i] == e) break; if(i >= 8) { // first leg not King step. Try Lion Dog 2+1 or 2-1 for(i=0; i<8; i++) if(f + 2*kStep[i] == e) break; if(i >= 8) return INVALID; // not even that if(f + 3*kStep[i] == t) t2 = SPECIAL + 72 + i; // 2+1 else if(f + kStep[i] == t) t2 = SPECIAL + 88 + i; // 2-1 else return INVALID; } else if(f + 3*kStep[i] == t) { // Lion Dog 1+2 move t2 = SPECIAL + 64 + i; } else if(*moveText == ',') { // 3rd leg follows! moveText++; if(f + 2*kStep[i] != t) return INVALID; // 3-leg moves must be linear! moveText += ReadSquare(moveText, &e); if(e != t) return INVALID; // must again continue with same piece moveText += ReadSquare(moveText, &t); if(f + 3*kStep[i] == t) t2 = SPECIAL + 80 + i; // 1+1+1 else if(f + kStep[i] == t) t2 = SPECIAL + 88 + i; // 2-1 entered as 1+1-1 else return INVALID; } else { for(j=0; j<8; j++) if(e + kStep[j] == t) break; if(j >= 8) return INVALID; // this rejects Lion Dog 1+2 moves! t2 = SPECIAL + 8*i + j; } } else if(chessFlag && board[f] != EMPTY && p[board[f]].value == pVal && board[t] == EMPTY) { // Pawn to empty, could be e.p. if(t == f + BW + 1) t2 = SPECIAL + 16; else if(t == f + BW - 1) t2 = SPECIAL + 48; else if(t == f - BW + 1) t2 = SPECIAL + 20; else if(t == f - BW - 1) t2 = SPECIAL + 52; // fake double-move } else if(currentVariant == V_LION && board[f] != EMPTY && p[board[f]].value == 280 && (t-f == 2 || f-t == 2)) { // castling if(t == f+2 && f < BW) t2 = CASTLE; else if(t == f-2 && f < BW) t2 = CASTLE + 1; else if(t == f+2 && f > BW) t2 = CASTLE + 2; else if(t == f-2 && f > BW) t2 = CASTLE + 3; } ret = f<> SQLEN & SQUARE)) break; // any null move matches @@@@ if((moveStack[i] & (PROMOTE | DEFER-1)) == ret) break; if((moveStack[i] & DEFER-1) == ret) deferred = i; // promoted version of entered non-promotion is legal } printf("# moveNr = %d in {%d,%d}\n", i, listStart, listEnd); if(i>=listEnd) { // no exact match if(deferred) { // but maybe non-sensical deferral int flags = p[board[f]].promoFlag; printf("# deferral of %d\n", deferred); i = deferred; // in any case we take that move if(!(flags & promoBoard[t] & (CANT_DEFER | LAST_RANK))) { // but change it into a deferral if that is allowed moveStack[i] &= ~PROMOTE; if(p[board[f]].value == 40) p[board[f]].promoFlag &= LAST_RANK; else if(!(flags & promoBoard[f]) && currentVariant != V_WOLF) moveStack[i] |= DEFER; // came from outside zone, so essential deferral } } if(i >= listEnd) { for(i=listStart; i= listEnd ? INVALID : moveStack[i]); } void Highlight(char *coords) { int i, j, n, sqr, cnt=0; char b[BSIZE], buf[2000], *q; for(i=0; i>SQLEN & SQUARE)) { int t = moveStack[i] & SQUARE; if(t >= CASTLE) t = toList[t - SPECIAL]; else // decode castling if(t >= SPECIAL) { int e = sqr + epList[t - SPECIAL]; // decode b[e] = 'C'; continue; } if(!b[t]) b[t] = (!boardCopy[t] ? 'Y' : 'R'); cnt++; if(moveStack[i] & PROMOTE) b[t] = 'M'; } } if(!cnt) { // no moves from given square if(sqr != lastPut) return; // refrain from sending empty FEN // we lifted a piece for second leg of move for(i=listStart; i>SQLEN & SQUARE)) { int e, t = moveStack[i] & SQUARE; if(t < SPECIAL) continue; // only special moves e = lastLift + epList[t - SPECIAL]; // decode if(sqr == lastLift + ep2List[t - SPECIAL]) { // second leg of 3-leg move b[e] = 'C'; cnt++; continue; } t = lastLift + toList[t - SPECIAL]; if(e != sqr) continue; if(!b[t]) b[t] = (!boardCopy[t] ? 'Y' : 'R'); cnt++; } } if(!cnt) return; } else lastLift = sqr; // remember lastPut = ABSENT; q = buf; for(i=BH-1; i>=0; i--) { for(j=0; j buf && q[-1] <= '9' && q[-1] >= '0') { q[-1]++; if(q[-1] > '9') { if(q > buf+1 && q[-2] <= '9' && q[-2] >= '0') q[-2]++; else q[-1] = '1', *q++ = '0'; } } else *q++ = '1'; } } *q++ = '/'; } q[-1] = 0; printf("highlight %s\n", buf); } int timeLeft; // timeleft on engine's clock int mps, timeControl, inc, timePerMove; // time-control parameters, to be used by Search char inBuf[8000], command[80], ponderMoveText[20]; void SetSearchTimes (int timeLeft) { int targetTime, movesLeft = BW*BH/4 + 20; if(mps) movesLeft = mps - (moveNr>>1)%mps; targetTime = (timeLeft - 1000*inc) / (movesLeft + 2) + 1000 * inc; if(moveNr < 30) targetTime *= 0.5 + moveNr/60.; // speedup in opening if(timePerMove > 0) targetTime = 0.4*timeLeft, movesLeft = 1; tlim1 = 0.4*targetTime; tlim2 = 2.4*targetTime; tlim3 = 5*timeLeft / (movesLeft + 4.1); printf("# limits %d, %d, %d mode = %d\n", tlim1, tlim2, tlim3, abortFlag); } int SearchBestMove (MOVE *move, MOVE *ponderMove) { int score; printf("# SearchBestMove\n"); startTime = GetTickCount(); nodes = 0; printf("# s=%d\n", startTime);fflush(stdout); MapFromScratch(attacks); retMove = INVALID; repCnt = 0; score = Search(-INF-1, INF+1, rootEval, maxDepth + QSdepth, 0, sup1, sup2, INF); *move = retMove; *ponderMove = pv[1]; printf("# best=%s\n", MoveToText(pv[0],0)); printf("# ponder=%s\n", MoveToText(pv[1],0)); return score; } int TakeBack(int n) { // reset the game and then replay it to the desired point int last, stm; last = moveNr - n; if(last < 0) last = 0; Init(SAME); stm = Setup2(startPos); printf("# setup done");fflush(stdout); for(moveNr=0; moveNr 0 && stm == WHITE || score < 0 && stm == BLACK) printf("1-0%s\n", tail); else printf("0-1%s\n", tail); } void GetLine(int root) { int i, c; while(1) { // wait for input, and read it until we have collected a complete line do { for(i = 0; (inBuf[i] = c = getchar()) != '\n'; i++) if(c == EOF || i>7997) exit(0); inBuf[i+1] = 0; } while(!i); // ignore empty lines // extract the first word sscanf(inBuf, "%s", command); printf("# in (mode = %d,%d): %s\n", root, abortFlag, command); if(!strcmp(command, "otim")) { continue; } // do not start pondering after receiving time commands, as move will follow immediately if(!strcmp(command, "time")) { sscanf(inBuf, "time %d", &timeLeft); continue; } if(!strcmp(command, "put")) { ReadSquare(inBuf+4, &lastPut); continue; } // ditto if(!strcmp(command, ".")) { inBuf[0] = 0; return; } // ignore for now if(!strcmp(command, "hover")) { inBuf[0] = 0; return; } // ignore for now if(!strcmp(command, "lift")) { inBuf[0] = 0; Highlight(inBuf+5); return; } // treat here if(!root && !strcmp(command, "usermove")) { printf("# move = %s#ponder = %s", inBuf+9, ponderMoveText); abortFlag = !!strcmp(inBuf+9, ponderMoveText); if(!abortFlag) { // ponder hit, continue as time-based search printf("# ponder hit\n"); SetSearchTimes(10*timeLeft + GetTickCount() - startTime); // add time we already have been pondering to total if(lastRootIter > tlim1) abortFlag = 2; // abort instantly if we are in iteration we should not have started inBuf[0] = 0; ponderMove = INVALID; return; } } abortFlag = 1; return; } } void TerminationCheck() { if(abortFlag < 0) { // check for input if(InputWaiting()) GetLine(0); // read & examine input command } else { // check for time if(GetTickCount() - startTime > tlim3) abortFlag = 2; } } int main() { int engineSide=NONE; // side played by engine MOVE move; int i, score, curVarNr = 0; setvbuf(stdin, NULL, _IOLBF, 1024); // buffering more than one line flaws test for pending input! Init(V_CHU); // Chu seed = startTime = GetTickCount(); moveNr = 0; // initialize random while(1) { // infinite loop fflush(stdout); // make sure everything is printed before we do something that might take time *inBuf = 0; if(moveNr >= 20) randomize = OFF; //if(moveNr >20) printf("resign\n"); #ifdef HASH if(hashMask) #endif if(listEnd == 0) ListMoves(); // always maintain a list of legal moves in root position abortFlag = -(ponder && WHITE+BLACK-stm == engineSide && moveNr); // pondering and opponent on move if(stm == engineSide || abortFlag && ponderMove) { // if it is the engine's turn to move, set it thinking, and let it move printf("# start search: stm=%d engine=%d (flag=%d)\n", stm, engineSide, abortFlag); if(abortFlag) { stm = MakeMove2(stm, ponderMove); // for pondering, play speculative move gameMove[moveNr++] = ponderMove; // remember in game sprintf(ponderMoveText, "%s\n", MoveToText(ponderMove, 0)); // for detecting ponder hits printf("# ponder move = %s", ponderMoveText); } else SetSearchTimes(10*timeLeft); // for thinking, schedule end time pboard(board); score = SearchBestMove(&move, &ponderMove); if(abortFlag == 1) { // ponder search was interrupted (and no hit) UnMake2(INVALID); moveNr--; stm ^= WHITE; // take ponder move back if we made one abortFlag = 0; } else if(move == INVALID) { // game apparently ended int kcapt = 0, xstm = stm ^ WHITE, king, k = p[king=royal[xstm]].pos; if( k != ABSENT) { // test if King capture possible if(attacks[2*k + stm]) { if( p[king + 2].pos == ABSENT ) kcapt = 1; // we have an attack on his only King } } else { // he has no king! Test for attacks on Crown Prince k = p[king + 2].pos; if(attacks[2*k + stm]) kcapt = 1; // we have attack on Crown Prince } if(kcapt) { // print King capture before claiming GenCapts(k, 0); printf("move %s\n", MoveToText(moveStack[msp-1], 1)); reason = "king capture"; } else reason = "resign"; engineSide = NONE; // so stop playing PrintResult(stm, score); } else { MOVE f, pMove = move; static char *pName[] = { "w", "z", "j" }; if((move & SQUARE) >= SPECIAL && p[board[f = move>>SQLEN & SQUARE]].value == pVal) { // e.p. capture pMove = move & ~SQUARE | f + toList[(move & SQUARE) - SPECIAL]; // print as a single move } stm = MakeMove2(stm, move); // assumes MakeMove returns new side to move gameMove[moveNr++] = move; // remember game i = p[undoInfo.victim].ranking; printf("move %s%s\n", MoveToText(pMove, 1), i == 5 && p[undoInfo.piece].ranking < 4 ? pName[i-5] : ""); listEnd = 0; continue; // go check if we should ponder } } else if(engineSide == ANALYZE || abortFlag) { // in analysis, we always ponder the position Move dummy; *ponderMoveText = 0; // forces miss on any move abortFlag = -1; // set pondering pvCuts = noCut; SearchBestMove(&dummy, &dummy); abortFlag = pvCuts = 0; } fflush(stdout); // make sure everything is printed before we do something that might take time if(!*inBuf) GetLine(1); // takes care of time and otim commands // recognize the command,and execute it if(!strcmp(command, "quit")) { break; } // breaks out of infinite loop if(!strcmp(command, "force")) { engineSide = NONE; continue; } if(!strcmp(command, "analyze")) { engineSide = ANALYZE; continue; } if(!strcmp(command, "exit")) { engineSide = NONE; continue; } if(!strcmp(command, "level")) { int min, sec=0; if(sscanf(inBuf, "level %d %d %d", &mps, &min, &inc) != 3) // if this does not work, it must be min:sec format sscanf(inBuf, "level %d %d:%d %d", &mps, &min, &sec, &inc); timeControl = 60*min + sec; timePerMove = -1; continue; } if(!strcmp(command, "protover")){ for(i=0; variants[i].boardWidth; i++) printf("%s%s", (i ? "," : "feature variants=\""), variants[i].name); printf("\"\n"); printf("feature ping=1 setboard=1 colors=0 usermove=1 memory=1 debug=1 sigint=0 sigterm=0\n"); printf("feature myname=\"HaChu " VERSION "\" highlight=1\n"); printf("feature option=\"Full analysis PV -check %d\"\n", noCut); // example of an engine-defined option printf("feature option=\"Allow repeats -check %d\"\n", allowRep); printf("feature option=\"Promote on entry -check %d\"\n", entryProm); printf("feature option=\"Okazaki rule -check %d\"\n", okazaki); printf("feature option=\"Resign -check %d\"\n", resign); // printf("feature option=\"Contempt -spin %d -200 200\"\n", contemptFactor); // and another one printf("feature option=\"Tsume -combo no /// Sente mates /// Gote mates\"\n"); printf("feature done=1\n"); continue; } if(!strcmp(command, "option")) { // setting of engine-define option; find out which if(sscanf(inBuf+7, "Full analysis PV=%d", &noCut) == 1) continue; if(sscanf(inBuf+7, "Allow repeats=%d", &allowRep) == 1) continue; if(sscanf(inBuf+7, "Resign=%d", &resign) == 1) continue; if(sscanf(inBuf+7, "Contempt=%d", &contemptFactor) == 1) continue; if(sscanf(inBuf+7, "Okazaki rule=%d", &okazaki) == 1) continue; if(sscanf(inBuf+7, "Promote on entry=%d", &entryProm) == 1) continue; if(sscanf(inBuf+7, "Tsume=%s", command) == 1) { if(!strcmp(command, "no")) tsume = 0; else if(!strcmp(command, "Sente")) tsume = 1; else if(!strcmp(command, "Gote")) tsume = 2; continue; } continue; } if(!strcmp(command, "sd")) { sscanf(inBuf, "sd %d", &maxDepth); continue; } if(!strcmp(command, "st")) { sscanf(inBuf, "st %d", &timePerMove); continue; } if(!strcmp(command, "memory")) { SetMemorySize(atoi(inBuf+7)); continue; } if(!strcmp(command, "ping")) { printf("pong%s", inBuf+4); continue; } // if(!strcmp(command, "")) { sscanf(inBuf, " %d", &); continue; } if(!strcmp(command, "easy")) { ponder = OFF; continue; } if(!strcmp(command, "hard")) { ponder = ON; continue; } if(!strcmp(command, "go")) { engineSide = stm; continue; } if(!strcmp(command, "post")) { postThinking = ON; continue; } if(!strcmp(command, "nopost")) { postThinking = OFF;continue; } if(!strcmp(command, "random")) { randomize = ON; continue; } if(!strcmp(command, "hint")) { if(ponderMove != INVALID) printf("Hint: %s\n", MoveToText(ponderMove, 0)); continue; } if(!strcmp(command, "book")) { continue; } // non-standard commands if(!strcmp(command, "p")) { pboard(board); continue; } if(!strcmp(command, "f")) { pbytes(fireBoard); continue; } if(!strcmp(command, "w")) { MapOneColor(WHITE, last[WHITE], attacks); pmap(attacks, WHITE); continue; } if(!strcmp(command, "b")) { MapOneColor(BLACK, last[BLACK], attacks); pmap(attacks, BLACK); continue; } if(!strcmp(command, "l")) { pplist(); continue; } // ignored commands: if(!strcmp(command, "xboard")) { continue; } if(!strcmp(command, "computer")){ comp = 1; continue; } if(!strcmp(command, "name")) { continue; } if(!strcmp(command, "ics")) { continue; } if(!strcmp(command, "accepted")){ continue; } if(!strcmp(command, "rejected")){ continue; } if(!strcmp(command, "result")) { engineSide = NONE; continue; } if(!strcmp(command, "hover")) { continue; } if(!strcmp(command, "")) { continue; } if(!strcmp(command, "usermove")){ int move = ParseMove(inBuf+9); pboard(board); if(move == INVALID) { if(reason) printf("Illegal move {%s}\n", reason); else printf("%s\n", reason="Illegal move"); if(comp) PrintResult(stm, -INF); // against computer: claim } else { stm = MakeMove2(stm, move); ponderMove = INVALID; listEnd = 0; gameMove[moveNr++] = move; // remember game } continue; } ponderMove = INVALID; // the following commands change the position, invalidating ponder move listEnd = 0; if(!strcmp(command, "new")) { engineSide = BLACK; Init(V_CHESS); stm = Setup2(NULL); maxDepth = MAXPLY; randomize = OFF; curVarNr = comp = 0; continue; } if(!strcmp(command, "variant")) { for(i=0; variants[i].boardWidth; i++) { sscanf(inBuf+8, "%s", command); if(!strcmp(variants[i].name, command)) { Init(curVarNr = i); stm = Setup2(NULL); break; } } if(currentVariant == V_WOLF) printf("setup (PNBR......................WKpnbr......................wk) 8x8+0_fairy rnbwkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBWKBNR w 0 1\n" "piece W& K3cpafK\n"); if(currentVariant == V_SHO) printf("setup (PNBRLSE..G.+++++++Kpnbrlse..g.+++++++k) 9x9+0_shogi lnsgkgsnl/1r2e2b1/ppppppppp/9/9/9/PPPPPPPPP/1B2E2R1/LNSGKGSNL w 0 1\n"); if(currentVariant == V_WA) printf("setup (P..^S^FV..^LW^OH.F.^R.E....R...D.GOL^M..^H.M.C.^CU.^W/.......^V.^P.^U..^DS.^GXK" "p..^s^fv..^lw^oh.f.^r.e....r...d.gol^m..^h.m.c.^cu.^w/.......^v.^p.^u..^ds.^gxk) 11x11+0_chu " "hmlcvkwgudo/1e3s3f1/ppprpppxppp/3p3p3/11/11/11/3P3P3/PPPXPPPRPPP/1F3S3E1/ODUGWKVCLMH w 0 1\n" "piece P& fW\npiece O& fR\npiece H& fRbW2\npiece U& fWbF\npiece L& fWbF\npiece M& vWfF\npiece G& vWfF\npiece C& sWfF\n" "piece +P& WfF\npiece +O& K\npiece +H& vN\npiece +U& BfW\npiece +L& vRfF3bFsW\npiece +M& FfW\npiece +G& sRvW\npiece +C& vRsWfF\n" "piece D& sbWfF\npiece V& FfW\npiece W& WfF\npiece S& sRvW\npiece R& FfRbW\npiece F& BfW\npiece X& FvWAvD\n" "piece +D& WfF\npiece +V& FfsW\npiece +W& K\npiece +S& R\npiece +R& FvWAvD\npiece +F& BvRsW\npiece E& vRfF3bFsW\n"); if(currentVariant == V_MACAD) printf("setup (P.*B*RQSEXOG....D^J'..*LP'.L!J'=J...*W!...*F'...^C.C.^L!.^P'^K.T*L'.*C!*H!^I'=Z.^E...*R'^P^T*W'*G'^G^SI'^X^OK" "p.*b*rqsexog....d^j'..*lp'.l!j'=j...*w!...*f'...^c.c.^l!.^p'^k.t*l'.*c!*h!^i'=z.^e...*r'^p^t*w'*g'^g^si'^x^ok) 13x13+0_chu %s w 0 1\n" "piece P& fW\npiece S& FfW\npiece E& FfsW\npiece X& WA\npiece O& FD\npiece G& WfF\npiece D& RF\npiece +J'& QNADcaKmabK\n" "piece L& fR\npiece P'& vW\npiece L!& KNADcaKmabK\npiece J'& blfFrW\npiece H!& RmasR\npiece W!& KADcavKmcpafmcpavK\n" "piece C!& BmasB\npiece F'& F2\npiece +C& vRfB\npiece C& vWfF\npiece +L!& K3NADcaKmabK\npiece +P'& vR\npiece T& FbsW\n" "piece L'& lfrbBfRbW\npiece +I'& QADcavKmcpafmcpavK\npiece +E& K\npiece R'& rflbBfRbW\npiece +P& WfF\npiece +T& BbsR\n" "piece W'& F3sW\npiece G'& W3fF\npiece +G& RfB\npiece +S& BfR\npiece I'& rbfFlW\npiece +X& F3vRsW\npiece +O& F3sRvW\n", macadArray); if(currentVariant == V_CASHEW) printf("setup (P.^K'^P'QS^W!XOGND'.HDT!...P'.L!E'.^W'K'W!..LF'V^E'J'H'...^L!^N..FT'L'C'G!H!D!I'.^H'..R'..^C'^F'..W'^X^OK" "p.^k'^p'qs^w!xognd'.hdt!...p'.l!e'.^w'k'w!..lf'v^e'j'h'...^l!^n..ft'l'c'g!h!d!i'.^h'..r'..^c'^f'..w'^x^ok) 13x13+0_chu %s w 0 1\n" "piece P& fW\npiece S& FfW\npiece W'& fFvWsW2\npiece E'& fFsWvW2\npiece X& WA\npiece O& FD\npiece G& WfF\npiece H& BW\n" "piece +C'& BW\npiece D& RF\npiece +F'& RF\npiece L& fR\npiece D'& fRbW\npiece L!& KNADcaKmabK\npiece +E'& KNADcaKmabK\n" "piece J'& FvlW\npiece H!& RmasR\npiece +W'& KADcavKmcpafmcpavK\npiece G!& WBmasB\npiece F'& F2\npiece +L!& K3NADcaKmabK\n" "piece T!& vRsW2flBfrF\npiece T'& fFvW2\npiece L'& lfrbBfRbW\npiece N& K\npiece R'& rflbBfRbW\npiece I'& FvrW\n" "piece +X& F3vRsW2\npiece +O& F3sRvW2\npiece H'& WfF2\npiece +H'& Q\npiece C'& F\npiece D!& sRvW2frBflF\npiece P'& sWfDbA\n" "piece K'& W2fF\npiece +K'& WBmasB\npiece +P'& RmasR\npiece +N& fRfBbF2bsW2\npiece F& FvW\npiece V& fF2sW\n", cashewArray); repStack[199] = hashKeyH, checkStack[199] = 0; continue; } if(!strcmp(command, "setboard")){ engineSide = NONE; Init(curVarNr); stm = Setup2(inBuf+9); continue; } if(!strcmp(command, "undo")) { stm = TakeBack(1); continue; } if(!strcmp(command, "remove")) { stm = TakeBack(2); continue; } printf("Error: unknown command\n"); } return 0; } hachu-0.21-29-gdf26f4a/hachu.eng000066400000000000000000000000761350040415400160370ustar00rootroot00000000000000plugin spec 0.0 hachu shogivariants,shatranj,asean,fairychess hachu-0.21-29-gdf26f4a/hachu.pod000066400000000000000000000050151350040415400160460ustar00rootroot00000000000000=head1 NAME hachu - xboard-compatible engine for Chu Shogi and other variants =head1 SYNOPSIS B =head1 DESCRIPTION B is a program that plays several shogi and chess variants, currently sho shogi, chu shogi, dai shogi, makruk and shatranj. In the future it might play more, and still larger shogi variants, in particular tenjiku shogi. HaChu uses the xboard/winboard chess-engine protocol to communicate. It thus can use XBoard as a graphical interface for the variants also supported by the latter. Only XBoard 4.8 and later support chu shogi, but sho shogi should already work under XBoard 4.7, and Makruk and Shatranj under much older versions. Dai shogi is not supported by XBoard yet. See xboard(6) for instructions about how to use B through xboard. To start up quickly, you just need the command: B. You can then use XBoard's New Variant dialog to select the variant you want to play. To play chu shogi, it is essential that XBoard's 'show target squares' option is switched on; otherwise the two-step lion moves can not be entered. Normally XBoard will use western-style chess symbols to represent the pieces. But HaChu comes with a set of piece images in the shape of the Japenese kanji for the names of the chu-shogi pieces. These can be used in XBoard to get an oriental-style board display. A settings file for configuring XBoard to use these pieces is also included with HaChu, so that you can use the command B to start XBoard with HaChu for oriental-style chu-shogi. =head1 OPTIONS =over 8 =item B HaChu supports some options that can only be set interactively, though XBoard's engine settings menu dialog. These include an option for solving tsume problems (checkmate problems where the winning side is only allowed to play checking moves). There are also options to adapt it to various versions of the chu-shogi rules: whether you can only promote on entering the promotion zone, or whether moves inside or out of the zone (after one move delay) can also be used for promotion, and whether repeats should be strictly forbidden, or only avoided like other losing moves. =back =head1 AVAILABILITY At http://hgm.nubati.net/cgi-bin/gitweb.cgi the source code can be obtained. =head1 SEE ALSO xboard(6) game rules: http://hgm.nubati.net/rules XBoard: http://hgm.nubati.net =head1 STANDARDS WinBoard, B(6) interface ("Chess Engine Communication Protocol") =head1 AUTHOR H.G.Muller . This manual page was generated with pod2man(1). hachu-0.21-29-gdf26f4a/logo.png000066400000000000000000000034251350040415400157230ustar00rootroot00000000000000‰PNG  IHDRd2%Wéé pHYsÃÃÇo¨d vpAgd2kǤ4PIDAThÞí›_H[WÇ‘D0Ã,ÃÐe-B* …†*ÉC(C‚"¡®Tó]q0ú´’ÓaORPôm}˜éƒCˆ’íÉÿ …8QÖÊš¬!+m÷prONÎ=çÜsoîuÆîKhÏ9÷Ü›ÜO¿ßù?uô­ŽÀÿ’“ÓÜm‡É,Õò1@w}IŒi=5†‡f3ÐÆ¼ªsÓÅ<Ïnfùý¤,‹²#nA¼®·ôa&³Z:¼žTKÛÚ[&c–VL CÉŒ ^®½ïü[>ªñ<¶T„ÇÌö7‘ÓË@%³ä-‹)ÓáÌK÷ ¸r’Ø×m—ÑÈËüÌ–ñwˆÝêŸèei}ЋɅ³Ãdö¸$¤kMˆÔŶUÑÊÐl†Ç ̺ä•Û²ÐëíÍþ†["©m7L •¼l¶/¿÷ÕdwI­õlž>ÉÕ8]7'COïã'u8x½»Tч…01 „xñ®Ú›^”_üwWNžì„}‹£]¥¸ÿx™ ù .¦¡Ù 造ZÙŶgcµÑ‡²²õÔ˜g,ŒöFÅ]Ü¿ÇPxè}°_öþ}ÞP(ù\ÂêÐ’Òúé”ê¤fî¢ÂÆê0ÅÔ‚ß\9™JãO)Èè¢ÜÉ@ñƒ—löu–­C¥µ,&)±¡]ÌÜE’Ô`rí"éG4ÃE« ,ŸL€êö|‰ÎcuIa‘8“k¸,ø-RØ·8ÚùÒÔR'éYŠ/åwÍ•™éÊŒ9X’¤6j_21‘¼Àµ“×ÑxïZΆ—ðwÚó% XT¦.IŠgPLdÈ%mß»€¼€ÎRKÓ‡Eb¹¼ ç”È4•àˆÚ—m…ÜGP{wÏ…FC¾ÌåYõ¹!6+ù E‰2+-)íU23iÕ ë-Ÿ}¥V‹ÕoæË^ño7²žÕâT™)Õˆ*èOŠŠú¼„¾™?‰0ŸÊk'Fú¨ª5êhýbdèürÓõ«£è[áe‚t´u!ŽmÄ‹kY$&dâ`/Pñ²+fÒŶÇÎ!RWÝOB2ý¤bV$5`Ÿq9¢4\}ûrZkVÚOÜÙ²÷ RÀƒ;7‰e…˜0¶²foÓ”ÓŒLºa™R…}‹é^€|)àÍc!mŽfTö¦¤»QÉ*6ÖIåKSdò¹_ä,æ´,œsi‘™}dا:×Ùð²¢¹¬$VÎ,}…&7ÔÎuPh¤pÁÜ×Pá©@ ¹övÕ‹hþ¬Õ¾¼§mÅ›iøÅ-5Ÿ†eɰÐ×Sc̰ÍJ›Ð;¢s>øIAAÁ?N%A[œskÒ}§î¡Î­ÇÞ2–OcÅF'ªªJß qÞ ¿ (æH¤üѨ̭ÜǖЍõ >gÙÈ7¢¿Ðtg½ùšÀ+þD E»Í ¡ÜŸ/y¯ûœãG}_'¬\Ús½óC@6“p"R}«#TN(™ˆJ /BðB{>q»?Á¼õÃ÷?~{ŒÊžŸ˜¤þ¹aí.O"7$ƒ3 g (_åE.ŽªsÓ*©Ø­þæÅ&g%ŸË~R€W Ýcˆ—A:„^ïÖ×ä`s2„ŽfRlž×‹!÷Ñ¥À“b—®Ä1äw«ÎÕ—t=ÏÈ%`BÞ\Q³ºv ã:ïÍ…­„ÅlÕå¢ËN—¨Iƒø_ëø‘O0½®Eæëc|)î_w—ܹtïNñTúLRívþ¼Q Êš5Îgáu}ddíôPC¡îÈHîòk1᪱cSêi)RC[ªÎ†ÓÅ€ZiÞy= æ» !mObí?_ùºU^¸CSž¡‘±•”] mše'¨^õMÉ\AI¤•ÿ²7÷f*×ÔÂêL­ý—5ëý 7D/@½†¹ã4X2ÎØFjŠY©gðö¼î‰ú׉;Àã- Ÿ¹u0¹fÉ´ù*ˆ›”bL24ª›Ø¸†f3Úe,C:þܦs –‚f³BS"`¹âE4t þãuPE[Ä¿Þ7p“Ý’ZV&­Lr[Ã%∊ï%§›“7¯Ô†õkð‚ƒïzóªÎ?„^ü×D2ü@bϪÝpódÖG«ËÛ°¸úsoáû%tEXtdate:create2014-09-13T14:37:43+02:00oÖ×%tEXtdate:modify2014-09-13T14:36:58+02:00?äƒ1IEND®B`‚hachu-0.21-29-gdf26f4a/svg/000077500000000000000000000000001350040415400150505ustar00rootroot00000000000000hachu-0.21-29-gdf26f4a/svg/BlackAdvisor.svg000066400000000000000000000165141350040415400201440ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackArchbishop.svg000066400000000000000000000164061350040415400206170ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackBishop.svg000066400000000000000000000122031350040415400177500ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackCanon.svg000066400000000000000000000117311350040415400175670ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackChancellor.svg000066400000000000000000000155431350040415400206100ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackClaw.svg000066400000000000000000000135261350040415400174230ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackCobra.svg000066400000000000000000000077671350040415400175750ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackCommoner.svg000066400000000000000000000165711350040415400203170ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackCrownedBishop.svg000066400000000000000000000147061350040415400213040ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackCrownedRook.svg000066400000000000000000000124541350040415400207700ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackDolphin.svg000066400000000000000000000222741350040415400201320ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackDrunk.svg000066400000000000000000000166731350040415400176260ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackElephant.svg000066400000000000000000000165261350040415400203000ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackGold.svg000066400000000000000000000142071350040415400174170ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackGoldPawn.svg000066400000000000000000000060051350040415400202420ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackHCrown.svg000066400000000000000000000160641350040415400177350ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackHSword.svg000066400000000000000000000155131350040415400177410ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackHawk.svg000066400000000000000000000223571350040415400174310ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackKing.svg000066400000000000000000000123751350040415400174260ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackKnight.svg000066400000000000000000000140061350040415400177530ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackKylin.svg000066400000000000000000000131671350040415400176240ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackLance.svg000066400000000000000000000123061350040415400175520ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackLeopard.svg000066400000000000000000000164011350040415400201160ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackLion.svg000066400000000000000000000130221350040415400174250ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackMarshall.svg000066400000000000000000000251331350040415400202750ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPawn.svg000066400000000000000000000121221350040415400174310ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPhoenix.svg000066400000000000000000000125361350040415400201470ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPrince.svg000066400000000000000000000107401350040415400177500ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPrincess.svg000066400000000000000000000106761350040415400203260ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoBishop.svg000066400000000000000000000123611350040415400207720ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoDragon.svg000066400000000000000000000126051350040415400207610ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoHSword.svg000066400000000000000000000156651350040415400207660ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoHorse.svg000066400000000000000000000150441350040415400206270ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoRook.svg000066400000000000000000000136031350040415400204600ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackPromoSword.svg000066400000000000000000000154211350040415400206440ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackQueen.svg000066400000000000000000000123721350040415400176100ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackRook.svg000066400000000000000000000134251350040415400174450ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackSword.svg000066400000000000000000000152531350040415400176320ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/BlackUnicorn.svg000066400000000000000000000204761350040415400201540ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteAdvisor.svg000066400000000000000000000162071350040415400202070ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteArchbishop.svg000066400000000000000000000165121350040415400206610ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteBishop.svg000066400000000000000000000117331350040415400200230ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteCanon.svg000066400000000000000000000116621350040415400176360ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteChancellor.svg000066400000000000000000000156521350040415400206550ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteClaw.svg000066400000000000000000000133221350040415400174610ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteCobra.svg000066400000000000000000000075701350040415400176310ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteCommoner.svg000066400000000000000000000162611350040415400203570ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteCrownedBishop.svg000066400000000000000000000146141350040415400213460ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteCrownedRook.svg000066400000000000000000000123771350040415400210400ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteDolphin.svg000066400000000000000000000217031350040415400201720ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteDrunk.svg000066400000000000000000000165141350040415400176640ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteElephant.svg000066400000000000000000000163511350040415400203400ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteGold.svg000066400000000000000000000137441350040415400174700ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteGoldPawn.svg000066400000000000000000000060321350040415400203060ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteHCrown.svg000066400000000000000000000160311350040415400177730ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteHSword.svg000066400000000000000000000153121350040415400200020ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteHawk.svg000066400000000000000000000221411350040415400174640ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteKing.svg000066400000000000000000000124111350040415400174610ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteKnight.svg000066400000000000000000000140751350040415400200250ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteKylin.svg000066400000000000000000000131351350040415400176630ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteLance.svg000066400000000000000000000122671350040415400176240ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteLeopard.svg000066400000000000000000000165201350040415400201640ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteLion.svg000066400000000000000000000127701350040415400175020ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteMarshall.svg000066400000000000000000000251621350040415400203430ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePawn.svg000066400000000000000000000117711350040415400175060ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePhoenix.svg000066400000000000000000000124661350040415400202150ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePrince.svg000066400000000000000000000106771350040415400200250ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePrincess.svg000066400000000000000000000107521350040415400203650ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoBishop.svg000066400000000000000000000121311350040415400210310ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoDragon.svg000066400000000000000000000125331350040415400210250ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoHSword.svg000066400000000000000000000154621350040415400210250ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoHorse.svg000066400000000000000000000147711350040415400207010ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoRook.svg000066400000000000000000000136201350040415400205230ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhitePromoSword.svg000066400000000000000000000153001350040415400207040ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteQueen.svg000066400000000000000000000123241350040415400176510ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteRook.svg000066400000000000000000000134451350040415400175130ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteSword.svg000066400000000000000000000151311350040415400176710ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/WhiteUnicorn.svg000066400000000000000000000202641350040415400202130ustar00rootroot00000000000000 image/svg+xml hachu-0.21-29-gdf26f4a/svg/chu000066400000000000000000000015351350040415400155560ustar00rootroot00000000000000; ; settings for oriental Chu-Shogi theme ; -variant chu ; ; pieces: supplied chu-shogi svg's, and flip them in flipView ; -pid ~~/themes/chu -flipBlack true -trueColors true ; ; board: no checkering (both w. and w.o. texture) ; -liteBackTextureFile "~~/themes/textures/wood_l.png" -darkBackTextureFile "~~/themes/textures/wood_l.png" -lightSquareColor #FF8040 -darkSquareColor #FF8040 ; ; detour under-promotion is less convenient when non-pawns promote ; -sweepPromotions false ; ; legality testing must be on for double-moves to work, no adjudication ; -testLegality true -trivialDraws false -materialDraws false ; ; make the Lion roar on double captures ; -soundRoar roar.wav ; ; redefine default engine ; -fcp hachu -scp hachu ; ; set up own persistence file, used for this theme only ; -settingsFile ~/.xboard-chu-rc -saveSettingsFile ~/.xboard-chu-rc hachu-0.21-29-gdf26f4a/svg/sho000066400000000000000000000006421350040415400155660ustar00rootroot00000000000000; ; additional settings for Sho Shogi ; -variant shogi ; ; size overrides ; -boardWidth 9 -boardHeight 9 -holdingsSize 0 ; ; add Elephant to piece set ; -pieceToCharTable "PNBRLSE..G.+++++++Kpnbrlse..g.+++++++k" ; ; provide initial position ; -loadPositionFile "~~/themes/conf/sho.fen" ; ; must play with legality testing off, for +E and mate ; -testLegality false ; ; redefine default engine ; -fcp hachu -scp hachu