| /* |
| C source for CHESS. |
| |
| Revision: 5-23-88 |
| |
| Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. |
| Copyright (c) 1988 John Stanback |
| |
| This file is part of CHESS. |
| |
| CHESS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY. No author or distributor |
| accepts responsibility to anyone for the consequences of using it |
| or for whether it serves any particular purpose or works at all, |
| unless he says so in writing. Refer to the CHESS General Public |
| License for full details. |
| |
| Everyone is granted permission to copy, modify and redistribute |
| CHESS, but only under the conditions described in the |
| CHESS General Public License. A copy of this license is |
| supposed to have been given to you along with CHESS so you |
| can know your rights and responsibilities. It should be in a |
| file named COPYING. Among other things, the copyright notice |
| and this notice must be preserved on all copies. |
| */ |
| |
| #include "plugin.h" |
| |
| #include "gnuchess.h" |
| #include "opening.h" |
| |
| #include <ctype.h> |
| |
| #define ttblsz 4096 |
| |
| #define ctlP 0x4000 |
| #define ctlN 0x2800 |
| #define ctlB 0x1800 |
| #define ctlR 0x0400 |
| #define ctlQ 0x0200 |
| #define ctlK 0x0100 |
| #define ctlBQ 0x1200 |
| #define ctlRQ 0x0600 |
| #define ctlNN 0x2000 |
| #define pxx " PNBRQK" |
| #define qxx " pnbrqk" |
| #define rxx "12345678" |
| #define cxx "abcdefgh" |
| #define check 0x0001 |
| #define capture 0x0002 |
| #define draw 0x0004 |
| #define promote 0x0008 |
| #define cstlmask 0x0010 |
| #define epmask 0x0020 |
| #define exact 0x0040 |
| #define pwnthrt 0x0080 |
| #define truescore 0x0001 |
| #define lowerbound 0x0002 |
| #define upperbound 0x0004 |
| #define maxdepth 30 |
| #define true 1 |
| #define false 0 |
| #define absv(x) (ABS(x)) |
| #define taxicab(a,b) (abs(column[a]-column[b]) + abs(row[a]-row[b])) |
| |
| /* ---- Chess datatypes and variables ---- */ |
| struct leaf |
| { |
| short f,t,score,reply; |
| unsigned short flags; |
| }; |
| struct BookEntry |
| { |
| struct BookEntry *next; |
| unsigned short *mv; |
| }; |
| struct hashval |
| { |
| unsigned long bd; |
| unsigned short key; |
| }; |
| struct hashentry |
| { |
| unsigned long hashbd; |
| unsigned short mv,flags; |
| short score,depth; |
| }; |
| |
| char mvstr1[5],mvstr2[5],mvstr3[6]; |
| struct leaf Tree[1500],*root; |
| short TrPnt[maxdepth],board[64],color[64]; |
| short row[64],column[64],locn[8][8],Pindex[64],svalue[64]; |
| short PieceList[2][16],PieceCnt[2],atak[2][64],PawnCnt[2][8]; |
| short castld[2],kingmoved[2],mtl[2],pmtl[2],emtl[2],hung[2]; |
| short c1,c2,*atk1,*atk2,*PC1,*PC2,EnemyKing; |
| short mate,post,opponent,computer,Sdepth,Awindow,Bwindow,dither; |
| bool withbook ; |
| long ResponseTime,ExtraTime,Level,et,et0,time0,cputimer,ft; |
| long NodeCnt,evrate,ETnodes,EvalNodes,HashCnt; |
| short quit,reverse,bothsides,hashflag,InChk,player,force,easy,beep; |
| short wking,bking,FROMsquare,TOsquare,timeout,Zscore,zwndw,xwndw,slk; |
| short INCscore; |
| short HasPawn[2],HasKnight[2],HasBishop[2],HasRook[2],HasQueen[2]; |
| short ChkFlag[maxdepth],CptrFlag[maxdepth],PawnThreat[maxdepth]; |
| short Pscore[maxdepth],Tscore[maxdepth],Threat[maxdepth]; |
| struct GameRec GameList[MAX_GAME_CNT]; |
| unsigned char GameCnt; /*Bug fix now rolls over instead of overflow*/ |
| short Game50,epsquare,lpost,rcptr,contempt; |
| short MaxSearchDepth,Xscore; |
| struct TimeControlRec TimeControl; |
| short TCflag,TCmoves,TCminutes,OperatorTime; |
| short otherside[3]={1,0,2}; |
| short rank7[3]={6,1,0}; |
| short map[64]= |
| {0,1,2,3,4,5,6,7, |
| 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, |
| 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, |
| 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, |
| 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, |
| 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, |
| 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, |
| 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77}; |
| short unmap[120]= |
| {0,1,2,3,4,5,6,7,-1,-1,-1,-1,-1,-1,-1,-1, |
| 8,9,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1, |
| 16,17,18,19,20,21,22,23,-1,-1,-1,-1,-1,-1,-1,-1, |
| 24,25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1, |
| 32,33,34,35,36,37,38,39,-1,-1,-1,-1,-1,-1,-1,-1, |
| 40,41,42,43,44,45,46,47,-1,-1,-1,-1,-1,-1,-1,-1, |
| 48,49,50,51,52,53,54,55,-1,-1,-1,-1,-1,-1,-1,-1, |
| 56,57,58,59,60,61,62,63}; |
| short Dcode[120]= |
| {0,1,1,1,1,1,1,1,0,0,0,0,0,0,0x0E,0x0F, |
| 0x10,0x11,0x12,0,0,0,0,0,0,0,0,0,0,0,0x0F,0x1F, |
| 0x10,0x21,0x11,0,0,0,0,0,0,0,0,0,0,0x0F,0,0, |
| 0x10,0,0,0x11,0,0,0,0,0,0,0,0,0x0F,0,0,0, |
| 0x10,0,0,0,0x11,0,0,0,0,0,0,0x0F,0,0,0,0, |
| 0x10,0,0,0,0,0x11,0,0,0,0,0x0F,0,0,0,0,0, |
| 0x10,0,0,0,0,0,0x11,0,0,0x0F,0,0,0,0,0,0, |
| 0x10,0,0,0,0,0,0,0x11}; |
| short Stboard[64]= |
| {rook,knight,bishop,queen,king,bishop,knight,rook, |
| pawn,pawn,pawn,pawn,pawn,pawn,pawn,pawn, |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| pawn,pawn,pawn,pawn,pawn,pawn,pawn,pawn, |
| rook,knight,bishop,queen,king,bishop,knight,rook}; |
| short Stcolor[64]= |
| {white,white,white,white,white,white,white,white, |
| white,white,white,white,white,white,white,white, |
| 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, |
| black,black,black,black,black,black,black,black, |
| black,black,black,black,black,black,black,black}; |
| short sweep[7]= {false,false,false,true,true,true,false}; |
| short Dpwn[3]={4,6,0}; |
| short Dstart[7]={6,4,8,4,0,0,0}; |
| short Dstop[7]={7,5,15,7,3,7,7}; |
| short Dir[16]={1,0x10,-1,-0x10,0x0F,0x11,-0x0F,-0x11, |
| 0x0E,-0x0E,0x12,-0x12,0x1F,-0x1F,0x21,-0x21}; |
| short Pdir[34]={0,0x38,0,0,0,0,0,0,0,0,0,0,0,0,0x02,0x35, |
| 0x38,0x35,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0x02, |
| 0,0x02}; |
| short pbit[7]={0,0x01,0x02,0x04,0x08,0x10,0x20}; |
| unsigned short killr0[maxdepth],killr1[maxdepth],killr2[maxdepth]; |
| unsigned short killr3[maxdepth],PrVar[maxdepth]; |
| unsigned short PV,hint,Swag0,Swag1,Swag2,Swag3,Swag4; |
| unsigned short hashkey; |
| unsigned long hashbd; |
| struct hashval hashcode[2][7][64]; |
| struct hashentry ttable[ttblsz]; |
| struct hashentry *ptbl; |
| unsigned char history[8192]; |
| |
| short Mwpawn[64],Mbpawn[64],Mknight[2][64],Mbishop[2][64]; |
| short Mking[2][64],Kfield[2][64]; |
| short value[7]={0,valueP,valueN,valueB,valueR,valueQ,valueK}; |
| short control[7]={0,ctlP,ctlN,ctlB,ctlR,ctlQ,ctlK}; |
| short PassedPawn0[8]={0,60,80,120,200,360,600,800}; |
| short PassedPawn1[8]={0,30,40,60,100,180,300,800}; |
| short PassedPawn2[8]={0,15,25,35,50,90,140,800}; |
| short PassedPawn3[8]={0,5,10,15,20,30,140,800}; |
| short ISOLANI[8] = {-12,-16,-20,-24,-24,-20,-16,-12}; |
| short BACKWARD[8] = {-6,-10,-15,-21,-28,-28,-28,-28}; |
| short BMBLTY[14] = {-2,0,2,4,6,8,10,12,13,14,15,16,16,16}; |
| short RMBLTY[14] = {0,2,4,6,8,10,11,12,13,14,14,14,14,14}; |
| short Kthreat[16] = {0,-8,-20,-36,-52,-68,-80,-80,-80,-80,-80,-80, |
| -80,-80,-80,-80}; |
| short KNIGHTPOST,KNIGHTSTRONG,BISHOPSTRONG,KATAK,KBNKsq; |
| short PEDRNK2B,PWEAKH,PADVNCM,PADVNCI,PAWNSHIELD,PDOUBLED,PBLOK; |
| short RHOPN,RHOPNX,KHOPN,KHOPNX,KSFTY; |
| short ATAKD,HUNGP,HUNGX,KCASTLD,KMOVD,XRAY,PINVAL; |
| short stage,stage2,Zwmtl,Zbmtl,Developed[2],PawnStorm; |
| short PawnBonus,BishopBonus,RookBonus; |
| short KingOpening[64]= |
| { 0, 0, -4,-10,-10, -4, 0, 0, |
| -4, -4, -8,-12,-12, -8, -4, -4, |
| -12,-16,-20,-20,-20,-20,-16,-12, |
| -16,-20,-24,-24,-24,-24,-20,-16, |
| -16,-20,-24,-24,-24,-24,-20,-16, |
| -12,-16,-20,-20,-20,-20,-16,-12, |
| -4, -4, -8,-12,-12, -8, -4, -4, |
| 0, 0, -4,-10,-10, -4, 0, 0}; |
| short KingEnding[64]= |
| { 0, 4, 8,12,12, 8, 4, 0, |
| 4,16,20,24,24,20,16, 4, |
| 8,20,28,32,32,28,20, 8, |
| 12,24,32,36,36,32,24,12, |
| 12,24,32,36,36,32,24,12, |
| 8,20,28,32,32,28,20, 8, |
| 4,16,20,24,24,20,16, 4, |
| 0, 4, 8,12,12, 8, 4, 0}; |
| short KBNK[64]= |
| {99,90,80,70,60,50,40,40, |
| 90,80,60,50,40,30,20,40, |
| 80,60,40,30,20,10,30,50, |
| 70,50,30,10, 0,20,40,60, |
| 60,40,20, 0,10,30,50,70, |
| 50,30,10,20,30,40,60,80, |
| 40,20,30,40,50,60,80,90, |
| 40,40,50,60,70,80,90,99}; |
| short pknight[64]= |
| { 0, 4, 8,10,10, 8, 4, 0, |
| 4, 8,16,20,20,16, 8, 4, |
| 8,16,24,28,28,24,16, 8, |
| 10,20,28,32,32,28,20,10, |
| 10,20,28,32,32,28,20,10, |
| 8,16,24,28,28,24,16, 8, |
| 4, 8,16,20,20,16, 8, 4, |
| 0, 4, 8,10,10, 8, 4, 0}; |
| short pbishop[64]= |
| {14,14,14,14,14,14,14,14, |
| 14,22,18,18,18,18,22,14, |
| 14,18,22,22,22,22,18,14, |
| 14,18,22,22,22,22,18,14, |
| 14,18,22,22,22,22,18,14, |
| 14,18,22,22,22,22,18,14, |
| 14,22,18,18,18,18,22,14, |
| 14,14,14,14,14,14,14,14}; |
| short PawnAdvance[64]= |
| { 0, 0, 0, 0, 0, 0, 0, 0, |
| 4, 4, 4, 0, 0, 4, 4, 4, |
| 6, 8, 2,10,10, 2, 8, 6, |
| 6, 8,12,16,16,12, 8, 6, |
| 8,12,16,24,24,16,12, 8, |
| 12,16,24,32,32,24,16,12, |
| 12,16,24,32,32,24,16,12, |
| 0, 0, 0, 0, 0, 0, 0, 0}; |
| |
| /* ............ prototypes ............ */ |
| void ScorePosition( short side, short *score ); |
| void ScoreLoneKing( short side, short *score ); |
| int ScoreKPK( short side, short winner, short loser, |
| short king1, short king2, short sq ); |
| int ScoreKBNK( short winner, short king1, short king2 ); |
| int SqValue( short sq, short side ); |
| void KingScan( short sq, short *s ); |
| void BRscan( short sq, short *s, short *mob ); |
| int trapped( short sq, short piece ); |
| void ExaminePosition( void ); |
| void UpdateWeights( void ); |
| int distance( short a, short b ); |
| void BlendBoard( short a[64] , short b[64] , short c[64] ); |
| void CopyBoard( short a[64] , short b[64] ); |
| |
| void OpeningBook ( void ); |
| int search ( short side, short ply, short depth, |
| short alpha, short beta, |
| unsigned short bstline[], short *rpt, |
| void (*callback)(void) ); |
| int evaluate ( short side, short xside, short ply, short depth, |
| short alpha, short beta ); |
| int ProbeTTable ( short side, short depth, |
| short *alpha, short *beta, short *score); |
| void PutInTTable ( short side, short score, short depth, |
| short alpha, short beta, unsigned short mv ); |
| void ZeroTTable ( void ); |
| void MoveList ( short side, short ply ); |
| |
| void GenMoves ( short ply, short sq, short side, short xside ); |
| void LinkMove ( short ply, short f, short t, short xside ); |
| void CaptureList ( short side, short xside, short ply ); |
| int castle ( short side, short kf, short kt, short iop ); |
| void EnPassant ( short xside, short f, short t, short iop ); |
| void MakeMove ( short side, struct leaf *node, |
| short *tempb, short *tempc, short *tempsf, short *tempst ); |
| void UnmakeMove ( short side, struct leaf *node, |
| short *tempb, short *tempc, short *tempsf, short *tempst ); |
| void UpdateHashbd ( short side, short piece, short f, short t ); |
| void UpdatePieceList ( short side, short sq, short iop ); |
| void InitializeStats ( void ); |
| void pick ( short p1, short p2 ); |
| void repetition ( short *cnt ); |
| int SqAtakd ( short sq, short side ); |
| void ataks ( short side, short *a ); |
| |
| void algbr ( short f, short t, short flag ); |
| void ElapsedTime( short iop); |
| |
| void NewGame(void); |
| |
| |
| /* ............ POSITIONAL EVALUATION ROUTINES ............ */ |
| |
| |
| void ScorePosition(side,score) |
| short side,*score; |
| |
| /* |
| Perform normal static evaluation of board position. A score is |
| generated for each piece and these are summed to get a score for each |
| side. |
| */ |
| |
| { |
| register short sq,s,i,xside; |
| short pscore[3]; |
| |
| wking = PieceList[white][0]; bking = PieceList[black][0]; |
| UpdateWeights(); |
| xside = otherside[side]; |
| pscore[white] = pscore[black] = 0; |
| |
| for (c1 = white; c1 <= black; c1++) |
| { |
| c2 = otherside[c1]; |
| if (c1 == white) EnemyKing = bking; else EnemyKing = wking; |
| atk1 = atak[c1]; atk2 = atak[c2]; |
| PC1 = PawnCnt[c1]; PC2 = PawnCnt[c2]; |
| for (i = 0; i <= PieceCnt[c1]; i++) |
| { |
| sq = PieceList[c1][i]; |
| s = SqValue(sq,side); |
| pscore[c1] += s; |
| svalue[sq] = s; |
| } |
| } |
| if (hung[side] > 1) pscore[side] += HUNGX; |
| if (hung[xside] > 1) pscore[xside] += HUNGX; |
| |
| *score = mtl[side] - mtl[xside] + pscore[side] - pscore[xside] + 10; |
| if (dither) *score += rb->rand() % dither; |
| |
| if (*score > 0 && pmtl[side] == 0) { |
| if (emtl[side] < valueR) { |
| *score = 0; |
| } else { |
| if (*score < valueR) *score /= 2; |
| } |
| } |
| if (*score < 0 && pmtl[xside] == 0) { |
| if (emtl[xside] < valueR) { |
| *score = 0; |
| } else { |
| if (-*score < valueR) *score /= 2; |
| } |
| } |
| |
| if (mtl[xside] == valueK && emtl[side] > valueB) *score += 200; |
| if (mtl[side] == valueK && emtl[xside] > valueB) *score -= 200; |
| } |
| |
| |
| void ScoreLoneKing(side,score) |
| short side,*score; |
| |
| /* |
| Static evaluation when loser has only a king and winner has no pawns |
| or no pieces. |
| */ |
| |
| { |
| register short winner,loser,king1,king2,s,i; |
| |
| UpdateWeights(); |
| if (mtl[white] > mtl[black]) winner = white; else winner = black; |
| loser = otherside[winner]; |
| king1 = PieceList[winner][0]; king2 = PieceList[loser][0]; |
| |
| s = 0; |
| |
| if (pmtl[winner] > 0) |
| for (i = 1; i <= PieceCnt[winner]; i++) |
| s += ScoreKPK(side,winner,loser,king1,king2,PieceList[winner][i]); |
| |
| else if (emtl[winner] == valueB+valueN) |
| s = ScoreKBNK(winner,king1,king2); |
| |
| else if (emtl[winner] > valueB) |
| s = 500 + emtl[winner] - 2*KingEnding[king2] - 2*distance(king1,king2); |
| |
| if (side == winner) *score = s; else *score = -s; |
| } |
| |
| |
| int ScoreKPK(side,winner,loser,king1,king2,sq) |
| short side,winner,loser,king1,king2,sq; |
| |
| /* |
| Score King and Pawns versus King endings. |
| */ |
| |
| { |
| register short s,r; |
| |
| if (PieceCnt[winner] == 1) s = 50; else s = 120; |
| if (winner == white) |
| { |
| if (side == loser) r = row[sq]-1; else r = row[sq]; |
| if (row[king2] >= r && distance(sq,king2) < 8-r) s += 10*row[sq]; |
| else s = 500+50*row[sq]; |
| if (row[sq] < 6) sq += 16; else sq += 8; |
| } |
| else |
| { |
| if (side == loser) r = row[sq]+1; else r = row[sq]; |
| if (row[king2] <= r && distance(sq,king2) < r+1) s += 10*(7-row[sq]); |
| else s = 500+50*(7-row[sq]); |
| if (row[sq] > 1) sq -= 16; else sq -= 8; |
| } |
| s += 8*(taxicab(king2,sq) - taxicab(king1,sq)); |
| return(s); |
| } |
| |
| |
| int ScoreKBNK(winner,king1,king2) |
| short winner,king1,king2; |
| |
| /* |
| Score King+Bishop+Knight versus King endings. |
| This doesn't work all that well but it's better than nothing. |
| */ |
| |
| { |
| register short s; |
| s = emtl[winner] - 300; |
| if (KBNKsq == 0) s += KBNK[king2]; |
| else s += KBNK[locn[row[king2]][7-column[king2]]]; |
| s -= taxicab(king1,king2); |
| s -= distance(PieceList[winner][1],king2); |
| s -= distance(PieceList[winner][2],king2); |
| return(s); |
| } |
| |
| |
| int SqValue(sq,side) |
| short sq,side; |
| |
| /* |
| Calculate the positional value for the piece on 'sq'. |
| */ |
| |
| { |
| register short j,fyle,rank,a1,a2; |
| short s,piece,in_square,r,mob,e,c; |
| |
| piece = board[sq]; |
| a1 = (atk1[sq] & 0x4FFF); a2 = (atk2[sq] & 0x4FFF); |
| rank = row[sq]; fyle = column[sq]; |
| s = 0; |
| if (piece == pawn && c1 == white) |
| { |
| s = Mwpawn[sq]; |
| if (sq == 11 || sq == 12) |
| if (color[sq+8] != neutral) s += PEDRNK2B; |
| if ((fyle == 0 || PC1[fyle-1] == 0) && |
| (fyle == 7 || PC1[fyle+1] == 0)) |
| s += ISOLANI[fyle]; |
| else if (PC1[fyle] > 1) s += PDOUBLED; |
| if (a1 < ctlP && atk1[sq+8] < ctlP) |
| { |
| s += BACKWARD[a2 & 0xFF]; |
| if (PC2[fyle] == 0) s += PWEAKH; |
| if (color[sq+8] != neutral) s += PBLOK; |
| } |
| if (PC2[fyle] == 0) |
| { |
| if (side == black) r = rank-1; else r = rank; |
| in_square = (row[bking] >= r && distance(sq,bking) < 8-r); |
| if (a2 == 0 || side == white) e = 0; else e = 1; |
| for (j = sq+8; j < 64; j += 8) |
| if (atk2[j] >= ctlP) { e = 2; break; } |
| else if (atk2[j] > 0 || color[j] != neutral) e = 1; |
| if (e == 2) s += (stage*PassedPawn3[rank]) / 10; |
| else if (in_square || e == 1) s += (stage*PassedPawn2[rank]) / 10; |
| else if (emtl[black] > 0) s += (stage*PassedPawn1[rank]) / 10; |
| else s += PassedPawn0[rank]; |
| } |
| } |
| else if (piece == pawn && c1 == black) |
| { |
| s = Mbpawn[sq]; |
| if (sq == 51 || sq == 52) |
| if (color[sq-8] != neutral) s += PEDRNK2B; |
| if ((fyle == 0 || PC1[fyle-1] == 0) && |
| (fyle == 7 || PC1[fyle+1] == 0)) |
| s += ISOLANI[fyle]; |
| else if (PC1[fyle] > 1) s += PDOUBLED; |
| if (a1 < ctlP && atk1[sq-8] < ctlP) |
| { |
| s += BACKWARD[a2 & 0xFF]; |
| if (PC2[fyle] == 0) s += PWEAKH; |
| if (color[sq-8] != neutral) s += PBLOK; |
| } |
| if (PC2[fyle] == 0) |
| { |
| if (side == white) r = rank+1; else r = rank; |
| in_square = (row[wking] <= r && distance(sq,wking) < r+1); |
| if (a2 == 0 || side == black) e = 0; else e = 1; |
| for (j = sq-8; j >= 0; j -= 8) |
| if (atk2[j] >= ctlP) { e = 2; break; } |
| else if (atk2[j] > 0 || color[j] != neutral) e = 1; |
| if (e == 2) s += (stage*PassedPawn3[7-rank]) / 10; |
| else if (in_square || e == 1) s += (stage*PassedPawn2[7-rank]) / 10; |
| else if (emtl[white] > 0) s += (stage*PassedPawn1[7-rank]) / 10; |
| else s += PassedPawn0[7-rank]; |
| } |
| } |
| else if (piece == knight) |
| { |
| s = Mknight[c1][sq]; |
| } |
| else if (piece == bishop) |
| { |
| s = Mbishop[c1][sq]; |
| BRscan(sq,&s,&mob); |
| s += BMBLTY[mob]; |
| } |
| else if (piece == rook) |
| { |
| s += RookBonus; |
| BRscan(sq,&s,&mob); |
| s += RMBLTY[mob]; |
| if (PC1[fyle] == 0) s += RHOPN; |
| if (PC2[fyle] == 0) s += RHOPNX; |
| if (rank == rank7[c1] && pmtl[c2] > 100) s += 10; |
| if (stage > 2) s += 14 - taxicab(sq,EnemyKing); |
| } |
| else if (piece == queen) |
| { |
| if (stage > 2) s += 14 - taxicab(sq,EnemyKing); |
| if (distance(sq,EnemyKing) < 3) s += 12; |
| } |
| else if (piece == king) |
| { |
| s = Mking[c1][sq]; |
| if (KSFTY > 0) |
| if (Developed[c2] || stage > 0) KingScan(sq,&s); |
| if (castld[c1]) s += KCASTLD; |
| else if (kingmoved[c1]) s += KMOVD; |
| |
| if (PC1[fyle] == 0) s += KHOPN; |
| if (PC2[fyle] == 0) s += KHOPNX; |
| if (fyle == 1 || fyle == 2 || fyle == 3 || fyle == 7) |
| { |
| if (PC1[fyle-1] == 0) s += KHOPN; |
| if (PC2[fyle-1] == 0) s += KHOPNX; |
| } |
| if (fyle == 4 || fyle == 5 || fyle == 6 || fyle == 0) |
| { |
| if (PC1[fyle+1] == 0) s += KHOPN; |
| if (PC2[fyle+1] == 0) s += KHOPNX; |
| } |
| if (fyle == 2) |
| { |
| if (PC1[0] == 0) s += KHOPN; |
| if (PC2[0] == 0) s += KHOPNX; |
| } |
| if (fyle == 5) |
| { |
| if (PC1[7] == 0) s += KHOPN; |
| if (PC2[7] == 0) s += KHOPNX; |
| } |
| } |
| |
| if (a2 > 0) |
| { |
| c = (control[piece] & 0x4FFF); |
| if (a1 == 0 || a2 > c+1) |
| { |
| s += HUNGP; |
| ++hung[c1]; |
| if (piece != king && trapped(sq,piece)) ++hung[c1]; |
| } |
| else if (piece != pawn || a2 > a1) |
| if (a2 >= c || a1 < ctlP) s += ATAKD; |
| } |
| return(s); |
| } |
| |
| |
| void KingScan(sq,s) |
| short sq,*s; |
| |
| /* |
| Assign penalties if king can be threatened by checks, if squares |
| near the king are controlled by the enemy (especially the queen), |
| or if there are no pawns near the king. |
| */ |
| |
| #define ScoreThreat\ |
| if (color[u] != c2) {\ |
| if (atk1[u] == 0 || (atk2[u] & 0xFF) > 1) {\ |
| ++cnt;\ |
| } else {\ |
| *s -= 3; \ |
| }\ |
| } |
| |
| { |
| register short m,u,d,i,m0,cnt,ok; |
| |
| cnt = 0; |
| m0 = map[sq]; |
| if (HasBishop[c2] || HasQueen[c2]) |
| for (i = Dstart[bishop]; i <= Dstop[bishop]; i++) |
| { |
| d = Dir[i]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| if (atk2[u] & ctlBQ) ScoreThreat |
| if (color[u] != neutral) break; |
| m += d; |
| } |
| } |
| if (HasRook[c2] || HasQueen[c2]) |
| for (i = Dstart[rook]; i <= Dstop[rook]; i++) |
| { |
| d = Dir[i]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| if (atk2[u] & ctlRQ) ScoreThreat |
| if (color[u] != neutral) break; |
| m += d; |
| } |
| } |
| if (HasKnight[c2]) |
| for (i = Dstart[knight]; i <= Dstop[knight]; i++) |
| if (!((m = m0+Dir[i]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (atk2[u] & ctlNN) ScoreThreat |
| } |
| *s += (KSFTY*Kthreat[cnt]) / 16; |
| |
| cnt = 0; ok = false; |
| m0 = map[sq]; |
| for (i = Dstart[king]; i <= Dstop[king]; i++) |
| if (!((m = m0+Dir[i]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (board[u] == pawn) ok = true; |
| if (atk2[u] > atk1[u]) |
| { |
| ++cnt; |
| if (atk2[u] & ctlQ) |
| if (atk2[u] > ctlQ+1 && atk1[u] < ctlQ) *s -= 4*KSFTY; |
| } |
| } |
| if (!ok) *s -= KSFTY; |
| if (cnt > 1) *s -= KSFTY; |
| } |
| |
| |
| void BRscan(sq,s,mob) |
| short sq,*s,*mob; |
| |
| /* |
| Find Bishop and Rook mobility, XRAY attacks, and pins. Increment the |
| hung[] array if a pin is found. |
| */ |
| |
| { |
| register short m,u,d,m0,j,piece,pin; |
| short *Kf; |
| |
| Kf = Kfield[c1]; |
| *mob = 0; |
| m0 = map[sq]; piece = board[sq]; |
| for (j = Dstart[piece]; j <= Dstop[piece]; j++) |
| { |
| pin = -1; |
| d = Dir[j]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; *s += Kf[u]; |
| if (color[u] == neutral) |
| { |
| (*mob)++; |
| m += d; |
| } |
| else if (pin < 0) |
| { |
| if (board[u] == pawn || board[u] == king) break; |
| pin = u; |
| m += d; |
| } |
| else if (color[u] == c2 && (board[u] > piece || atk2[u] == 0)) |
| { |
| if (color[pin] == c2) |
| { |
| *s += PINVAL; |
| if (atk2[pin] == 0 || |
| atk1[pin] > control[board[pin]]+1) |
| ++hung[c2]; |
| } |
| else *s += XRAY; |
| break; |
| } |
| else break; |
| } |
| } |
| } |
| |
| |
| int trapped(sq,piece) |
| short sq,piece; |
| |
| /* |
| See if the attacked piece has unattacked squares to move to. |
| */ |
| |
| { |
| register short u,m,d,i,m0; |
| |
| m0 = map[sq]; |
| if (sweep[piece]) |
| for (i = Dstart[piece]; i <= Dstop[piece]; i++) |
| { |
| d = Dir[i]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] == c1) break; |
| if (atk2[u] == 0 || board[u] >= piece) return(false); |
| if (color[u] == c2) break; |
| m += d; |
| } |
| } |
| else if (piece == pawn) |
| { |
| if (c1 == white) u = sq+8; else u = sq-8; |
| if (color[u] == neutral && atk1[u] >= atk2[u]) |
| return(false); |
| if (!((m = m0+Dir[Dpwn[c1]]) & 0x88)) |
| if (color[unmap[m]] == c2) return(false); |
| if (!((m = m0+Dir[Dpwn[c1]+1]) & 0x88)) |
| if (color[unmap[m]] == c2) return(false); |
| } |
| else |
| { |
| for (i = Dstart[piece]; i <= Dstop[piece]; i++) |
| if (!((m = m0+Dir[i]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] != c1) |
| if (atk2[u] == 0 || board[u] >= piece) return(false); |
| } |
| } |
| return(true); |
| } |
| |
| |
| void ExaminePosition() |
| |
| /* |
| This is done one time before the search is started. Set up arrays |
| Mwpawn, Mbpawn, Mknight, Mbishop, Mking which are used in the |
| SqValue() function to determine the positional value of each piece. |
| */ |
| |
| { |
| register short i,sq; |
| short wpadv,bpadv,wstrong,bstrong,z,side,pp,j,val,Pd,fyle,rank; |
| |
| wking = PieceList[white][0]; bking = PieceList[black][0]; |
| ataks(white,atak[white]); ataks(black,atak[black]); |
| Zwmtl = Zbmtl = 0; |
| UpdateWeights(); |
| HasPawn[white] = HasPawn[black] = 0; |
| HasKnight[white] = HasKnight[black] = 0; |
| HasBishop[white] = HasBishop[black] = 0; |
| HasRook[white] = HasRook[black] = 0; |
| HasQueen[white] = HasQueen[black] = 0; |
| for (side = white; side <= black; side++) |
| for (i = 0; i <= PieceCnt[side]; i++) |
| switch (board[PieceList[side][i]]) |
| { |
| case pawn : ++HasPawn[side]; break; |
| case knight : ++HasKnight[side]; break; |
| case bishop : ++HasBishop[side]; break; |
| case rook : ++HasRook[side]; break; |
| case queen : ++HasQueen[side]; break; |
| } |
| if (!Developed[white]) |
| Developed[white] = (board[1] != knight && board[2] != bishop && |
| board[5] != bishop && board[6] != knight); |
| if (!Developed[black]) |
| Developed[black] = (board[57] != knight && board[58] != bishop && |
| board[61] != bishop && board[62] != knight); |
| if (!PawnStorm && stage < 5) |
| PawnStorm = ((column[wking] < 3 && column[bking] > 4) || |
| (column[wking] > 4 && column[bking] < 3)); |
| |
| CopyBoard(pknight,Mknight[white]); |
| CopyBoard(pknight,Mknight[black]); |
| CopyBoard(pbishop,Mbishop[white]); |
| CopyBoard(pbishop,Mbishop[black]); |
| BlendBoard(KingOpening,KingEnding,Mking[white]); |
| BlendBoard(KingOpening,KingEnding,Mking[black]); |
| |
| for (sq = 0; sq < 64; sq++) |
| { |
| fyle = column[sq]; rank = row[sq]; |
| wstrong = bstrong = true; |
| for (i = sq; i < 64; i += 8) |
| if (atak[black][i] >= ctlP) wstrong = false; |
| for (i = sq; i >= 0; i -= 8) |
| if (atak[white][i] >= ctlP) bstrong = false; |
| wpadv = bpadv = PADVNCM; |
| if ((fyle == 0 || PawnCnt[white][fyle-1] == 0) && |
| (fyle == 7 || PawnCnt[white][fyle+1] == 0)) wpadv = PADVNCI; |
| if ((fyle == 0 || PawnCnt[black][fyle-1] == 0) && |
| (fyle == 7 || PawnCnt[black][fyle+1] == 0)) bpadv = PADVNCI; |
| Mwpawn[sq] = (wpadv*PawnAdvance[sq]) / 10; |
| Mbpawn[sq] = (bpadv*PawnAdvance[63-sq]) / 10; |
| Mwpawn[sq] += PawnBonus; Mbpawn[sq] += PawnBonus; |
| if (castld[white] || kingmoved[white]) |
| { |
| if ((fyle < 3 || fyle > 4) && distance(sq,wking) < 3) |
| Mwpawn[sq] += PAWNSHIELD; |
| } |
| else if (rank < 3 && (fyle < 2 || fyle > 5)) |
| Mwpawn[sq] += PAWNSHIELD / 2; |
| if (castld[black] || kingmoved[black]) |
| { |
| if ((fyle < 3 || fyle > 4) && distance(sq,bking) < 3) |
| Mbpawn[sq] += PAWNSHIELD; |
| } |
| else if (rank > 4 && (fyle < 2 || fyle > 5)) |
| Mbpawn[sq] += PAWNSHIELD / 2; |
| if (PawnStorm) |
| { |
| if ((column[wking] < 4 && fyle > 4) || |
| (column[wking] > 3 && fyle < 3)) Mwpawn[sq] += 3*rank - 21; |
| if ((column[bking] < 4 && fyle > 4) || |
| (column[bking] > 3 && fyle < 3)) Mbpawn[sq] -= 3*rank; |
| } |
| |
| Mknight[white][sq] += 5 - distance(sq,bking); |
| Mknight[white][sq] += 5 - distance(sq,wking); |
| Mknight[black][sq] += 5 - distance(sq,wking); |
| Mknight[black][sq] += 5 - distance(sq,bking); |
| Mbishop[white][sq] += BishopBonus; |
| Mbishop[black][sq] += BishopBonus; |
| for (i = 0; i <= PieceCnt[black]; i++) |
| if (distance(sq,PieceList[black][i]) < 3) |
| Mknight[white][sq] += KNIGHTPOST; |
| for (i = 0; i <= PieceCnt[white]; i++) |
| if (distance(sq,PieceList[white][i]) < 3) |
| Mknight[black][sq] += KNIGHTPOST; |
| if (wstrong) Mknight[white][sq] += KNIGHTSTRONG; |
| if (bstrong) Mknight[black][sq] += KNIGHTSTRONG; |
| if (wstrong) Mbishop[white][sq] += BISHOPSTRONG; |
| if (bstrong) Mbishop[black][sq] += BISHOPSTRONG; |
| |
| if (HasBishop[white] == 2) Mbishop[white][sq] += 8; |
| if (HasBishop[black] == 2) Mbishop[black][sq] += 8; |
| if (HasKnight[white] == 2) Mknight[white][sq] += 5; |
| if (HasKnight[black] == 2) Mknight[black][sq] += 5; |
| |
| if (board[sq] == bishop) { |
| if (rank % 2 == fyle % 2) { |
| KBNKsq = 0; |
| } else { |
| KBNKsq = 7; |
| } |
| } |
| |
| Kfield[white][sq] = Kfield[black][sq] = 0; |
| if (distance(sq,wking) == 1) Kfield[black][sq] = KATAK; |
| if (distance(sq,bking) == 1) Kfield[white][sq] = KATAK; |
| |
| Pd = 0; |
| for (i = 0; i < 64; i++) |
| if (board[i] == pawn) |
| { |
| if (color[i] == white) |
| { |
| pp = true; |
| if (row[i] == 6) z = i+8; else z = i+16; |
| for (j = i+8; j < 64; j += 8) |
| if (atak[black][j] > ctlP || board[j] == pawn) pp = false; |
| } |
| else |
| { |
| pp = true; |
| if (row[i] == 1) z = i-8; else z = i-16; |
| for (j = i-8; j >= 0; j -= 8) |
| if (atak[white][j] > ctlP || board[j] == pawn) pp = false; |
| } |
| if (pp) Pd += 5*taxicab(sq,z); else Pd += taxicab(sq,z); |
| } |
| if (Pd != 0) |
| { |
| val = (Pd*stage2) / 10; |
| Mking[white][sq] -= val; |
| Mking[black][sq] -= val; |
| } |
| } |
| } |
| |
| |
| void UpdateWeights() |
| |
| /* |
| If material balance has changed, determine the values for the |
| positional evaluation terms. |
| */ |
| |
| { |
| register short tmtl; |
| |
| if (mtl[white] != Zwmtl || mtl[black] != Zbmtl) |
| { |
| Zwmtl = mtl[white]; Zbmtl = mtl[black]; |
| emtl[white] = Zwmtl - pmtl[white] - valueK; |
| emtl[black] = Zbmtl - pmtl[black] - valueK; |
| tmtl = emtl[white] + emtl[black]; |
| if (tmtl > 6600) stage = 0; |
| else if (tmtl < 1400) stage = 10; |
| else stage = (6600-tmtl) / 520; |
| if (tmtl > 3600) stage2 = 0; |
| else if (tmtl < 1400) stage2 = 10; |
| else stage2 = (3600-tmtl) / 220; |
| |
| PEDRNK2B = -15; /* centre pawn on 2nd rank & blocked */ |
| PBLOK = -4; /* blocked backward pawn */ |
| PDOUBLED = -14; /* doubled pawn */ |
| PWEAKH = -4; /* weak pawn on half open file */ |
| PAWNSHIELD = 10-stage; /* pawn near friendly king */ |
| PADVNCM = 10; /* advanced pawn multiplier */ |
| PADVNCI = 7; /* muliplier for isolated pawn */ |
| PawnBonus = stage; |
| |
| KNIGHTPOST = (stage+2)/3; /* knight near enemy pieces */ |
| KNIGHTSTRONG = (stage+6)/2; /* occupies pawn hole */ |
| |
| BISHOPSTRONG = (stage+6)/2; /* occupies pawn hole */ |
| BishopBonus = 2*stage; |
| |
| RHOPN = 10; /* rook on half open file */ |
| RHOPNX = 4; |
| RookBonus = 6*stage; |
| |
| XRAY = 8; /* Xray attack on piece */ |
| PINVAL = 10; /* Pin */ |
| |
| KHOPN = (3*stage-30) / 2; /* king on half open file */ |
| KHOPNX = KHOPN / 2; |
| KCASTLD = 10 - stage; |
| KMOVD = -40 / (stage+1); /* king moved before castling */ |
| KATAK = (10-stage) / 2; /* B,R attacks near enemy king */ |
| if (stage < 8) KSFTY = 16-2*stage; else KSFTY = 0; |
| |
| ATAKD = -6; /* defender > attacker */ |
| HUNGP = -8; /* each hung piece */ |
| HUNGX = -12; /* extra for >1 hung piece */ |
| } |
| } |
| |
| |
| int distance(a,b) |
| short a,b; |
| { |
| register short d1,d2; |
| |
| d1 = abs(column[a]-column[b]); |
| d2 = abs(row[a]-row[b]); |
| if (d1 > d2) return(d1); else return(d2); |
| } |
| |
| |
| void BlendBoard(a,b,c) |
| short a[64],b[64],c[64]; |
| { |
| register int sq; |
| for (sq = 0; sq < 64; sq++) |
| c[sq] = (a[sq]*(10-stage) + b[sq]*stage) / 10; |
| } |
| |
| |
| void CopyBoard(a,b) |
| short a[64],b[64]; |
| { |
| register int sq; |
| for (sq = 0; sq < 64; sq++) |
| b[sq] = a[sq]; |
| } |
| |
| /* ............ MOVE GENERATION & SEARCH ROUTINES .............. */ |
| |
| |
| int SelectMove( short side, short iop , void (*callback)(void), char* move_buffer) |
| |
| /* |
| Select a move by calling function search() at progressively deeper |
| ply until time is up or a mate or draw is reached. An alpha-beta |
| window of -90 to +90 points is set around the score returned from the |
| previous iteration. If Sdepth != 0 then the program has correctly |
| predicted the opponents move and the search will start at a depth of |
| Sdepth+1 rather than a depth of 1. |
| */ |
| |
| { |
| static short i,alpha,beta,score,tempb,tempc,tempsf,tempst,xside,rpt; |
| |
| timeout = false; |
| xside = otherside[side]; |
| if (iop != 2) player = side; |
| if (TCflag) |
| { |
| ResponseTime = (TimeControl.clock[side]) / |
| (TimeControl.moves[side] + 3) - |
| OperatorTime; |
| ResponseTime += (ResponseTime*TimeControl.moves[side])/(2*TCmoves+1); |
| } |
| else ResponseTime = Level; |
| if (iop == 2) ResponseTime = 999; |
| if (Sdepth > 0 && root->score > Zscore-zwndw) ResponseTime -= ft; |
| else if (ResponseTime < 1) ResponseTime = 1; |
| ExtraTime = 0; |
| ExaminePosition(); |
| ScorePosition(side,&score); |
| Pscore[0] = -score; |
| |
| if (Sdepth == 0) |
| { |
| ZeroTTable(); |
| /*SearchStartStuff(side);*/ |
| for (i = 0; i < 8192; i++) history[i] = 0; |
| FROMsquare = TOsquare = -1; |
| PV = 0; |
| if (iop != 2) hint = 0; |
| for (i = 0; i < maxdepth; i++) |
| PrVar[i] = killr0[i] = killr1[i] = killr2[i] = killr3[i] = 0; |
| |
| alpha = -9000; beta = 9000; |
| rpt = 0; |
| TrPnt[1] = 0; root = &Tree[0]; |
| MoveList(side,1); |
| for (i = TrPnt[1]; i < TrPnt[2]; i++) pick(i,TrPnt[2]-1); |
| if (withbook) OpeningBook(); |
| if (withbook) timeout = true; |
| NodeCnt = ETnodes = EvalNodes = HashCnt = 0; |
| Zscore = 0; zwndw = 20; |
| } |
| |
| while (!timeout && Sdepth < MaxSearchDepth) |
| { |
| Sdepth++; |
| /*ShowDepth(' ');*/ |
| score = search(side,1,Sdepth,alpha,beta,PrVar,&rpt,callback); |
| for (i = 1; i <= Sdepth; i++) killr0[i] = PrVar[i]; |
| if (score < alpha && !timeout) |
| { |
| /*ShowDepth('-');*/ |
| ExtraTime = 10*ResponseTime; |
| ZeroTTable(); |
| score = search(side,1,Sdepth,-9000,beta,PrVar,&rpt,callback); |
| } |
| if (score > beta && !timeout && !(root->flags & exact)) |
| { |
| /*ShowDepth('+');*/ |
| ExtraTime = 0; |
| ZeroTTable(); |
| score = search(side,1,Sdepth,alpha,9000,PrVar,&rpt,callback); |
| } |
| score = root->score; |
| if (!timeout) |
| for (i = TrPnt[1]+1; i < TrPnt[2]; i++) pick(i,TrPnt[2]-1); |
| /*ShowResults(score,PrVar,'.');*/ |
| for (i = 1; i <= Sdepth; i++) killr0[i] = PrVar[i]; |
| if (score > Zscore-zwndw && score > Tree[1].score+250) ExtraTime = 0; |
| else if (score > Zscore-3*zwndw) ExtraTime = ResponseTime; |
| else ExtraTime = 3*ResponseTime; |
| if (root->flags & exact) timeout = true; |
| if (Tree[1].score < -9000) timeout = true; |
| if (4*et > 2*ResponseTime + ExtraTime) timeout = true; |
| if (!timeout) |
| { |
| Tscore[0] = score; |
| if (Zscore == 0) Zscore = score; |
| else Zscore = (Zscore+score)/2; |
| } |
| zwndw = 20+abs(Zscore/12); |
| beta = score + Bwindow; |
| if (Zscore < score) alpha = Zscore - Awindow - zwndw; |
| else alpha = score - Awindow - zwndw; |
| } |
| |
| score = root->score; |
| if (rpt >= 2 || score < -12000) root->flags |= draw; |
| if (iop == 2) return(0); |
| if (!withbook) hint = PrVar[2]; |
| ElapsedTime(1); |
| |
| if (score > -9999 && rpt <= 2) |
| { |
| MakeMove(side,root,&tempb,&tempc,&tempsf,&tempst); |
| algbr(root->f,root->t,root->flags & cstlmask); |
| } |
| else mvstr1[0] = '\0'; |
| /*OutputMove();*/ |
| |
| short index; |
| for (index=0;index<5;index++){ |
| move_buffer[index] = mvstr1[index]; |
| } |
| if (SqAtakd(PieceList[(side==white)?black:white][0],side)){ |
| move_buffer[4] = '+'; |
| move_buffer[5] = '\0'; |
| } |
| |
| if (score == -9999 || score == 9998) mate = true; |
| if (mate) hint = 0; |
| if (root->flags & cstlmask) Game50 = GameCnt; |
| else if (board[root->t] == pawn || (root->flags & capture)) |
| Game50 = GameCnt; |
| GameList[GameCnt].score = score; |
| GameList[GameCnt].nodes = NodeCnt; |
| GameList[GameCnt].time = (short)et; |
| GameList[GameCnt].depth = Sdepth; |
| if (TCflag) |
| { |
| TimeControl.clock[side] -= (et + OperatorTime); |
| if (--TimeControl.moves[side] == 0) SetTimeControl(); |
| } |
| if ((root->flags & draw) && bothsides) quit = true; |
| if (GameCnt > MAX_GAME_CNT - 2) quit = true; |
| player = xside; |
| Sdepth = 0; |
| return(0); |
| } |
| |
| |
| void OpeningBook() |
| |
| /* |
| Go thru each of the opening lines of play and check for a match with |
| the current game listing. If a match occurs, generate a random number. |
| If this number is the largest generated so far then the next move in |
| this line becomes the current "candidate". After all lines are |
| checked, the candidate move is put at the top of the Tree[] array and |
| will be played by the program. Note that the program does not handle |
| book transpositions. |
| */ |
| |
| { |
| short j,pnt; |
| unsigned short m; |
| unsigned r,r0; |
| int o_c=0 , m_c=0 ; |
| |
| rb->srand ( *rb->current_tick ) ; |
| r0 = 0; |
| m = 0; |
| while ( o_c < MAX_OPENING ) { |
| m_c = 0 ; |
| for (j = 0; j <= GameCnt; j++) { |
| if ( GameList[j].gmove != OBook[o_c][m_c] ) break; |
| m_c++; |
| } |
| /* I added ( m != OBook[o_c][m_c] ) trying to get more random games */ |
| if ( ( j > GameCnt ) && ( m != OBook[o_c][m_c] ) ) { |
| r=rb->rand(); |
| if ( r > r0 ) { |
| r0 = r; m = OBook[o_c][m_c]; |
| hint = OBook[o_c][m_c+1]; |
| } |
| } |
| o_c++; |
| } |
| |
| for (pnt = TrPnt[1]; pnt < TrPnt[2]; pnt++) |
| if ((Tree[pnt].f<<8) + Tree[pnt].t == m) Tree[pnt].score = 0; |
| pick(TrPnt[1],TrPnt[2]-1); |
| if (Tree[TrPnt[1]].score < 0) withbook = false; |
| } |
| |
| |
| #define UpdateSearchStatus \ |
| {\ |
| if (pnt > TrPnt[1])\ |
| {\ |
| d = best-Zscore; e = best-node->score;\ |
| if (best < alpha) ExtraTime = 10*ResponseTime;\ |
| else if (d > -zwndw && e > 4*zwndw) ExtraTime = -ResponseTime/3;\ |
| else if (d > -zwndw) ExtraTime = 0;\ |
| else if (d > -3*zwndw) ExtraTime = ResponseTime;\ |
| else if (d > -9*zwndw) ExtraTime = 3*ResponseTime;\ |
| else ExtraTime = 5*ResponseTime;\ |
| }\ |
| } |
| |
| int search( short side, short ply, short depth, |
| short alpha, short beta, unsigned short bstline[], short *rpt, |
| void (*callback)(void) ) |
| |
| /* |
| Perform an alpha-beta search to determine the score for the current |
| board position. If depth <= 0 only capturing moves, pawn promotions |
| and responses to check are generated and searched, otherwise all |
| moves are processed. The search depth is modified for check evasions, |
| certain re-captures and threats. Extensions may continue for up to 11 |
| ply beyond the nominal search depth. |
| */ |
| |
| #define prune (cf && score+node->score < alpha) |
| #define ReCapture (rcptr && score > alpha && score < beta &&\ |
| ply > 2 && CptrFlag[ply-1] && CptrFlag[ply-2] &&\ |
| depth == Sdepth-ply+1) |
| #define Parry (hung[side] > 1 && ply == Sdepth+1) |
| #define MateThreat (ply < Sdepth+4 && ply > 4 &&\ |
| ChkFlag[ply-2] && ChkFlag[ply-4] &&\ |
| ChkFlag[ply-2] != ChkFlag[ply-4]) |
| |
| { |
| register short j,pnt; |
| short best,tempb,tempc,tempsf,tempst; |
| short xside,pbst,d,e,cf,score,rcnt; |
| unsigned short mv,nxtline[maxdepth]; |
| struct leaf *node,tmp; |
| |
| /* this is the only place we need to yield */ |
| rb->yield(); |
| /* and check for user interaction */ |
| callback(); |
| |
| NodeCnt++; |
| xside = otherside[side]; |
| |
| if (ply <= Sdepth+3) repetition(rpt); else *rpt = 0; |
| if (*rpt >= 2) return(0); |
| |
| score = evaluate(side,xside,ply,depth,alpha,beta); |
| if (score > 9000) return(score); |
| |
| if (depth > 0) |
| { |
| if (InChk || PawnThreat[ply-1] || ReCapture) ++depth; |
| } |
| else |
| { |
| if (score >= alpha && |
| (InChk || PawnThreat[ply-1] || Parry)) depth = 1; |
| else if (score <= beta && MateThreat) depth = 1; |
| } |
| |
| PV = 0; |
| if (depth > 0 && hashflag && ply > 1) |
| { |
| ProbeTTable(side,depth,&alpha,&beta,&score); |
| bstline[ply] = PV; |
| bstline[ply+1] = 0; |
| if (beta == -20000) return(score); |
| if (alpha > beta) return(alpha); |
| } |
| |
| if (Sdepth == 1) d = 7; else d = 11; |
| if (ply > Sdepth+d || (depth <= 0 && score > beta)) return(score); |
| |
| if (ply > 1) { |
| if (depth > 0) { |
| MoveList(side,ply); |
| } else { |
| CaptureList(side,xside,ply); |
| } |
| } |
| |
| if (TrPnt[ply] == TrPnt[ply+1]) return(score); |
| |
| cf = (depth < 1 && ply > Sdepth+1 && !ChkFlag[ply-2] && !slk); |
| |
| if (depth > 0) best = -12000; else best = score; |
| if (best > alpha) alpha = best; |
| |
| for (pnt = pbst = TrPnt[ply]; |
| pnt < TrPnt[ply+1] && best <= beta; |
| pnt++) |
| { |
| if (ply > 1) pick(pnt,TrPnt[ply+1]-1); |
| node = &Tree[pnt]; |
| mv = (node->f << 8) + node->t; |
| nxtline[ply+1] = 0; |
| |
| if (prune) break; |
| if (ply == 1) UpdateSearchStatus; |
| |
| if (!(node->flags & exact)) |
| { |
| MakeMove(side,node,&tempb,&tempc,&tempsf,&tempst); |
| CptrFlag[ply] = (node->flags & capture); |
| PawnThreat[ply] = (node->flags & pwnthrt); |
| Tscore[ply] = node->score; |
| node->score = -search(xside,ply+1,depth-1,-beta,-alpha, |
| nxtline,&rcnt,callback); |
| if (abs(node->score) > 9000) node->flags |= exact; |
| else if (rcnt == 1) node->score /= 2; |
| if (rcnt >= 2 || GameCnt-Game50 > 99 || |
| (node->score == 9999-ply && !ChkFlag[ply])) |
| { |
| node->flags |= draw; node->flags |= exact; |
| if (side == computer) node->score = contempt; |
| else node->score = -contempt; |
| } |
| UnmakeMove(side,node,&tempb,&tempc,&tempsf,&tempst); |
| } |
| if (node->score > best && !timeout) |
| { |
| if (depth > 0) |
| if (node->score > alpha && !(node->flags & exact)) |
| node->score += depth; |
| best = node->score; pbst = pnt; |
| if (best > alpha) alpha = best; |
| for (j = ply+1; nxtline[j] > 0; j++) bstline[j] = nxtline[j]; |
| bstline[j] = 0; |
| bstline[ply] = mv; |
| if (ply == 1) |
| { |
| if (best == alpha) |
| { |
| tmp = Tree[pnt]; |
| for (j = pnt-1; j >= 0; j--) Tree[j+1] = Tree[j]; |
| Tree[0] = tmp; |
| pbst = 0; |
| } |
| /*if (Sdepth > 2) |
| if (best > beta) ShowResults(best,bstline,'+'); |
| else if (best < alpha) ShowResults(best,bstline,'-'); |
| else ShowResults(best,bstline,'&');*/ |
| } |
| } |
| if (NodeCnt > ETnodes) ElapsedTime(0); |
| if (timeout) return(-Tscore[ply-1]); |
| } |
| |
| node = &Tree[pbst]; |
| mv = (node->f<<8) + node->t; |
| if (hashflag && ply <= Sdepth && *rpt == 0 && best == alpha) |
| PutInTTable(side,best,depth,alpha,beta,mv); |
| if (depth > 0) |
| { |
| j = (node->f<<6) + node->t; if (side == black) j |= 0x1000; |
| if (history[j] < 150) history[j] += 2*depth; |
| if (node->t != (GameList[GameCnt].gmove & 0xFF)) { |
| if (best <= beta) { |
| killr3[ply] = mv; |
| } else { |
| if (mv != killr1[ply]) { |
| killr2[ply] = killr1[ply]; |
| killr1[ply] = mv; |
| } |
| } |
| } |
| if (best > 9000) killr0[ply] = mv; else killr0[ply] = 0; |
| } |
| return(best); |
| } |
| |
| |
| int evaluate(side,xside,ply,depth,alpha,beta) |
| short side,xside,ply,depth,alpha,beta; |
| |
| /* |
| Compute an estimate of the score by adding the positional score from |
| the previous ply to the material difference. If this score falls |
| inside a window which is 180 points wider than the alpha-beta window |
| (or within a 50 point window during quiescence search) call |
| ScorePosition() to determine a score, otherwise return the estimated |
| score. If one side has only a king and the other either has no pawns |
| or no pieces then the function ScoreLoneKing() is called. |
| */ |
| |
| { |
| register short evflag; |
| |
| Xscore = -Pscore[ply-1] - INCscore + mtl[side] - mtl[xside]; |
| hung[white] = hung[black] = 0; |
| slk = ((mtl[white] == valueK && (pmtl[black] == 0 || emtl[black] == 0)) || |
| (mtl[black] == valueK && (pmtl[white] == 0 || emtl[white] == 0))); |
| |
| if (slk) evflag = false; |
| else evflag = |
| (ply == 1 || ply < Sdepth || |
| (depth == 0 && Xscore > alpha-xwndw && Xscore < beta+xwndw) || |
| (depth < 0 && Xscore > alpha-25 && Xscore < beta+25)); |
| |
| if (evflag) |
| { |
| EvalNodes++; |
| ataks(side,atak[side]); |
| if (atak[side][PieceList[xside][0]] > 0) return(10001-ply); |
| ataks(xside,atak[xside]); |
| InChk = (atak[xside][PieceList[side][0]] > 0); |
| ScorePosition(side,&Xscore); |
| } |
| else |
| { |
| if (SqAtakd(PieceList[xside][0],side)) return(10001-ply); |
| InChk = SqAtakd(PieceList[side][0],xside); |
| if (slk) ScoreLoneKing(side,&Xscore); |
| } |
| |
| Pscore[ply] = Xscore - mtl[side] + mtl[xside]; |
| if (InChk) ChkFlag[ply-1] = Pindex[TOsquare]; |
| else ChkFlag[ply-1] = 0; |
| return(Xscore); |
| } |
| |
| |
| int ProbeTTable(side,depth,alpha,beta,score) |
| short side,depth,*alpha,*beta,*score; |
| |
| /* |
| Look for the current board position in the transposition table. |
| */ |
| |
| { |
| short hindx; |
| if (side == white) hashkey |= 1; else hashkey &= 0xFFFE; |
| hindx = (hashkey & (ttblsz-1)); |
| ptbl = (ttable + hindx); |
| if (ptbl->depth >= depth && ptbl->hashbd == hashbd) |
| { |
| HashCnt++; |
| PV = ptbl->mv; |
| if (ptbl->flags & truescore) |
| { |
| *score = ptbl->score; |
| *beta = -20000; |
| return(true); |
| } |
| /* |
| else if (ptbl->flags & upperbound) |
| { |
| if (ptbl->score < *beta) *beta = ptbl->score+1; |
| } |
| */ |
| else if (ptbl->flags & lowerbound) |
| { |
| if (ptbl->score > *alpha) *alpha = ptbl->score-1; |
| } |
| } |
| return(false); |
| } |
| |
| |
| void PutInTTable(side,score,depth,alpha,beta,mv) |
| short side,score,depth,alpha,beta; |
| unsigned short mv; |
| |
| /* |
| Store the current board position in the transposition table. |
| */ |
| |
| { |
| short hindx; |
| if (side == white) hashkey |= 1; else hashkey &= 0xFFFE; |
| hindx = (hashkey & (ttblsz-1)); |
| ptbl = (ttable + hindx); |
| ptbl->hashbd = hashbd; |
| ptbl->depth = depth; |
| ptbl->score = score; |
| ptbl->mv = mv; |
| ptbl->flags = 0; |
| if (score < alpha) ptbl->flags |= upperbound; |
| else if (score > beta) ptbl->flags |= lowerbound; |
| else ptbl->flags |= truescore; |
| } |
| |
| |
| void ZeroTTable() |
| { |
| int i; |
| if (hashflag) |
| for (i = 0; i < ttblsz; i++) |
| { |
| ptbl = (ttable + i); |
| ptbl->depth = 0; |
| } |
| } |
| |
| |
| void MoveList(side,ply) |
| short side,ply; |
| |
| /* |
| Fill the array Tree[] with all available moves for side to play. Array |
| TrPnt[ply] contains the index into Tree[] of the first move at a ply. |
| */ |
| |
| { |
| register short i,xside,f; |
| |
| xside = otherside[side]; |
| if (PV == 0) Swag0 = killr0[ply]; else Swag0 = PV; |
| Swag1 = killr1[ply]; Swag2 = killr2[ply]; |
| Swag3 = killr3[ply]; Swag4 = 0; |
| if (ply > 2) Swag4 = killr1[ply-2]; |
| TrPnt[ply+1] = TrPnt[ply]; |
| Dstart[pawn] = Dpwn[side]; Dstop[pawn] = Dstart[pawn] + 1; |
| for (i = PieceCnt[side]; i >= 0; i--) |
| GenMoves(ply,PieceList[side][i],side,xside); |
| if (kingmoved[side] == 0 && !castld[side]) |
| { |
| f = PieceList[side][0]; |
| if (castle(side,f,f+2,0)) |
| { |
| LinkMove(ply,f,f+2,xside); |
| Tree[TrPnt[ply+1]-1].flags |= cstlmask; |
| } |
| if (castle(side,f,f-2,0)) |
| { |
| LinkMove(ply,f,f-2,xside); |
| Tree[TrPnt[ply+1]-1].flags |= cstlmask; |
| } |
| } |
| } |
| |
| |
| void GenMoves(ply,sq,side,xside) |
| short ply,sq,side,xside; |
| |
| /* |
| Generate moves for a piece. The from square is mapped onto a special |
| board and offsets (taken from array Dir[]) are added to the mapped |
| location. The newly generated square is tested to see if it falls off |
| the board by ANDing the square with 88 HEX. Legal moves are linked |
| into the tree. |
| */ |
| |
| { |
| register short m,u,d,i,m0,piece; |
| |
| piece = board[sq]; m0 = map[sq]; |
| if (sweep[piece]) |
| for (i = Dstart[piece]; i <= Dstop[piece]; i++) |
| { |
| d = Dir[i]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] == neutral) |
| { |
| LinkMove(ply,sq,u,xside); |
| m += d; |
| } |
| else if (color[u] == xside) |
| { |
| LinkMove(ply,sq,u,xside); |
| break; |
| } |
| else break; |
| } |
| } |
| else if (piece == pawn) |
| { |
| if (side == white && color[sq+8] == neutral) |
| { |
| LinkMove(ply,sq,sq+8,xside); |
| if (row[sq] == 1) |
| if (color[sq+16] == neutral) |
| LinkMove(ply,sq,sq+16,xside); |
| } |
| else if (side == black && color[sq-8] == neutral) |
| { |
| LinkMove(ply,sq,sq-8,xside); |
| if (row[sq] == 6) |
| if (color[sq-16] == neutral) |
| LinkMove(ply,sq,sq-16,xside); |
| } |
| for (i = Dstart[piece]; i <= Dstop[piece]; i++) |
| if (!((m = m0+Dir[i]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] == xside || u == epsquare) |
| LinkMove(ply,sq,u,xside); |
| } |
| } |
| else |
| { |
| for (i = Dstart[piece]; i <= Dstop[piece]; i++) |
| if (!((m = m0+Dir[i]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] != side) LinkMove(ply,sq,u,xside); |
| } |
| } |
| } |
| |
| |
| void LinkMove(ply,f,t,xside) |
| short ply,f,t,xside; |
| |
| /* |
| Add a move to the tree. Assign a bonus to order the moves |
| as follows: |
| 1. Principle variation |
| 2. Capture of last moved piece |
| 3. Other captures (major pieces first) |
| 4. Killer moves |
| 5. "history" killers |
| */ |
| |
| { |
| register short s,z; |
| register unsigned short mv; |
| struct leaf *node; |
| |
| node = &Tree[TrPnt[ply+1]]; |
| ++TrPnt[ply+1]; |
| node->flags = 0; |
| node->f = f; node->t = t; |
| mv = (f<<8) + t; |
| s = 0; |
| if (mv == Swag0) s = 2000; |
| else if (mv == Swag1) s = 60; |
| else if (mv == Swag2) s = 50; |
| else if (mv == Swag3) s = 40; |
| else if (mv == Swag4) s = 30; |
| if (color[t] != neutral) |
| { |
| node->flags |= capture; |
| if (t == TOsquare) s += 500; |
| s += value[board[t]] - board[f]; |
| } |
| if (board[f] == pawn) { |
| if (row[t] == 0 || row[t] == 7) { |
| node->flags |= promote; |
| s += 800; |
| } else { |
| if (row[t] == 1 || row[t] == 6) { |
| node->flags |= pwnthrt; |
| s += 600; |
| } else { |
| if (t == epsquare) node->flags |= epmask; |
| } |
| } |
| } |
| z = (f<<6) + t; if (xside == white) z |= 0x1000; |
| s += history[z]; |
| node->score = s - 20000; |
| } |
| |
| |
| void CaptureList(side,xside,ply) |
| short side,xside,ply; |
| |
| /* |
| Generate captures and Pawn promotions only. |
| */ |
| |
| #define LinkCapture \ |
| {\ |
| node->f = sq; node->t = u;\ |
| node->flags = capture;\ |
| node->score = value[board[u]] + svalue[board[u]] - piece;\ |
| if (piece == pawn && (u < 8 || u > 55))\ |
| {\ |
| node->flags |= promote;\ |
| node->score = valueQ;\ |
| }\ |
| ++node;\ |
| ++TrPnt[ply+1];\ |
| } |
| |
| { |
| register short m,u,d,sq,m0; |
| short i,j,j1,j2,r7,d0,piece,*PL; |
| struct leaf *node; |
| |
| TrPnt[ply+1] = TrPnt[ply]; |
| node = &Tree[TrPnt[ply]]; |
| Dstart[pawn] = Dpwn[side]; Dstop[pawn] = Dstart[pawn] + 1; |
| if (side == white) |
| { |
| r7 = 6; d0 = 8; |
| } |
| else |
| { |
| r7 = 1; d0 = -8; |
| } |
| PL = PieceList[side]; |
| for (i = 0; i <= PieceCnt[side]; i++) |
| { |
| sq = PL[i]; |
| m0 = map[sq]; piece = board[sq]; |
| j1 = Dstart[piece]; j2 = Dstop[piece]; |
| if (sweep[piece]) |
| for (j = j1; j <= j2; j++) |
| { |
| d = Dir[j]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] == neutral) m += d; |
| else |
| { |
| if (color[u] == xside) LinkCapture; |
| break; |
| } |
| } |
| } |
| else |
| { |
| for (j = j1; j <= j2; j++) |
| if (!((m = m0+Dir[j]) & 0x88)) |
| { |
| u = unmap[m]; |
| if (color[u] == xside) LinkCapture; |
| } |
| if (piece == pawn && row[sq] == r7) |
| { |
| u = sq+d0; |
| if (color[u] == neutral) LinkCapture; |
| } |
| } |
| } |
| } |
| |
| |
| int castle(side,kf,kt,iop) |
| short side,kf,kt,iop; |
| |
| /* |
| Make or Unmake a castling move. |
| */ |
| |
| { |
| register short rf,rt,d,t0,xside; |
| |
| xside = otherside[side]; |
| if (kt > kf) |
| { |
| rf = kf+3; rt = kt-1; d = 1; |
| } |
| else |
| { |
| rf = kf-4; rt = kt+1; d = -1; |
| } |
| if (iop == 0) |
| { |
| if (board[kf] != king || board[rf] != rook || color[rf] != side) |
| return(false); |
| if (color[kt] != neutral || color[rt] != neutral) return(false); |
| if (d == -1 && color[kt+d] != neutral) return(false); |
| if (SqAtakd(kf,xside)) return(false); |
| if (SqAtakd(kt,xside)) return(false); |
| if (SqAtakd(kf+d,xside)) return(false); |
| } |
| else |
| { |
| if (iop == 1) castld[side] = true; else castld[side] = false; |
| if (iop == 2) |
| { |
| t0 = kt; kt = kf; kf = t0; |
| t0 = rt; rt = rf; rf = t0; |
| } |
| board[kt] = king; color[kt] = side; Pindex[kt] = 0; |
| board[kf] = no_piece; color[kf] = neutral; |
| board[rt] = rook; color[rt] = side; Pindex[rt] = Pindex[rf]; |
| board[rf] = no_piece; color[rf] = neutral; |
| PieceList[side][Pindex[kt]] = kt; |
| PieceList[side][Pindex[rt]] = rt; |
| if (hashflag) |
| { |
| UpdateHashbd(side,king,kf,kt); |
| UpdateHashbd(side,rook,rf,rt); |
| } |
| } |
| return(true); |
| } |
| |
| |
| void EnPassant(xside,f,t,iop) |
| short xside,f,t,iop; |
| |
| /* |
| Make or unmake an en passant move. |
| */ |
| |
| { |
| register short l; |
| if (t > f) l = t-8; else l = t+8; |
| if (iop == 1) |
| { |
| board[l] = no_piece; color[l] = neutral; |
| } |
| else |
| { |
| board[l] = pawn; color[l] = xside; |
| } |
| InitializeStats(); |
| } |
| |
| |
| void MakeMove(side,node,tempb,tempc,tempsf,tempst) |
| short side,*tempc,*tempb,*tempsf,*tempst; |
| struct leaf *node; |
| |
| /* |
| Update Arrays board[], color[], and Pindex[] to reflect the new board |
| position obtained after making the move pointed to by node. Also |
| update miscellaneous stuff that changes when a move is made. |
| */ |
| |
| { |
| register short f,t,xside,ct,cf; |
| |
| xside = otherside[side]; |
| f = node->f; t = node->t; epsquare = -1; |
| FROMsquare = f; TOsquare = t; |
| INCscore = 0; |
| GameList[++GameCnt].gmove = (f<<8) + t; |
| if (node->flags & cstlmask) |
| { |
| GameList[GameCnt].piece = no_piece; |
| GameList[GameCnt].color = side; |
| castle(side,f,t,1); |
| } |
| else |
| { |
| *tempc = color[t]; *tempb = board[t]; |
| *tempsf = svalue[f]; *tempst = svalue[t]; |
| GameList[GameCnt].piece = *tempb; |
| GameList[GameCnt].color = *tempc; |
| if (*tempc != neutral) |
| { |
| UpdatePieceList(*tempc,t,1); |
| if (*tempb == pawn) --PawnCnt[*tempc][column[t]]; |
| if (board[f] == pawn) |
| { |
| --PawnCnt[side][column[f]]; |
| ++PawnCnt[side][column[t]]; |
| cf = column[f]; ct = column[t]; |
| if (PawnCnt[side][ct] > 1+PawnCnt[side][cf]) |
| INCscore -= 15; |
| else if (PawnCnt[side][ct] < 1+PawnCnt[side][cf]) |
| INCscore += 15; |
| else if (ct == 0 || ct == 7 || PawnCnt[side][ct+ct-cf] == 0) |
| INCscore -= 15; |
| } |
| mtl[xside] -= value[*tempb]; |
| if (*tempb == pawn) pmtl[xside] -= valueP; |
| if (hashflag) UpdateHashbd(xside,*tempb,-1,t); |
| INCscore += *tempst; |
| } |
| color[t] = color[f]; board[t] = board[f]; svalue[t] = svalue[f]; |
| Pindex[t] = Pindex[f]; PieceList[side][Pindex[t]] = t; |
| color[f] = neutral; board[f] = no_piece; |
| if (board[t] == pawn) { |
| if (t-f == 16) { |
| epsquare = f+8; |
| } else { |
| if (f-t == 16) epsquare = f-8; |
| } |
| } |
| if (node->flags & promote) |
| { |
| board[t] = queen; |
| --PawnCnt[side][column[t]]; |
| mtl[side] += valueQ - valueP; |
| pmtl[side] -= valueP; |
| HasQueen[side] = true; |
| if (hashflag) |
| { |
| UpdateHashbd(side,pawn,f,-1); |
| UpdateHashbd(side,queen,f,-1); |
| } |
| INCscore -= *tempsf; |
| } |
| if (board[t] == king) ++kingmoved[side]; |
| if (node->flags & epmask) EnPassant(xside,f,t,1); |
| else if (hashflag) UpdateHashbd(side,board[t],f,t); |
| } |
| } |
| |
| |
| void UnmakeMove(side,node,tempb,tempc,tempsf,tempst) |
| short side,*tempc,*tempb,*tempsf,*tempst; |
| struct leaf *node; |
| |
| /* |
| Take back a move. |
| */ |
| |
| { |
| register short f,t,xside; |
| |
| xside = otherside[side]; |
| f = node->f; t = node->t; epsquare = -1; |
| GameCnt--; |
| if (node->flags & cstlmask) castle(side,f,t,2); |
| else |
| { |
| color[f] = color[t]; board[f] = board[t]; svalue[f] = *tempsf; |
| Pindex[f] = Pindex[t]; PieceList[side][Pindex[f]] = f; |
| color[t] = *tempc; board[t] = *tempb; svalue[t] = *tempst; |
| if (node->flags & promote) |
| { |
| board[f] = pawn; |
| ++PawnCnt[side][column[t]]; |
| mtl[side] += valueP - valueQ; |
| pmtl[side] += valueP; |
| if (hashflag) |
| { |
| UpdateHashbd(side,queen,-1,t); |
| UpdateHashbd(side,pawn,-1,t); |
| } |
| } |
| if (*tempc != neutral) |
| { |
| UpdatePieceList(*tempc,t,2); |
| if (*tempb == pawn) ++PawnCnt[*tempc][column[t]]; |
| if (board[f] == pawn) |
| { |
| --PawnCnt[side][column[t]]; |
| ++PawnCnt[side][column[f]]; |
| } |
| mtl[xside] += value[*tempb]; |
| if (*tempb == pawn) pmtl[xside] += valueP; |
| if (hashflag) UpdateHashbd(xside,*tempb,-1,t); |
| } |
| if (board[f] == king) --kingmoved[side]; |
| if (node->flags & epmask) EnPassant(xside,f,t,2); |
| else if (hashflag) UpdateHashbd(side,board[f],f,t); |
| } |
| } |
| |
| |
| void UpdateHashbd(side,piece,f,t) |
| short side,piece,f,t; |
| |
| /* |
| hashbd contains a 32 bit "signature" of the board position. hashkey |
| contains a 16 bit code used to address the hash table. When a move is |
| made, XOR'ing the hashcode of moved piece on the from and to squares |
| with the hashbd and hashkey values keeps things current. |
| */ |
| |
| { |
| if (f >= 0) |
| { |
| hashbd ^= hashcode[side][piece][f].bd; |
| hashkey ^= hashcode[side][piece][f].key; |
| } |
| if (t >= 0) |
| { |
| hashbd ^= hashcode[side][piece][t].bd; |
| hashkey ^= hashcode[side][piece][t].key; |
| } |
| } |
| |
| |
| void UpdatePieceList(side,sq,iop) |
| short side,sq,iop; |
| |
| /* |
| Update the PieceList and Pindex arrays when a piece is captured or |
| when a capture is unmade. |
| */ |
| |
| { |
| register short i; |
| if (iop == 1) |
| { |
| PieceCnt[side]--; |
| for (i = Pindex[sq]; i <= PieceCnt[side]; i++) |
| { |
| PieceList[side][i] = PieceList[side][i+1]; |
| Pindex[PieceList[side][i]] = i; |
| } |
| } |
| else |
| { |
| PieceCnt[side]++; |
| PieceList[side][PieceCnt[side]] = sq; |
| Pindex[sq] = PieceCnt[side]; |
| } |
| } |
| |
| |
| void InitializeStats() |
| |
| /* |
| Scan thru the board seeing what's on each square. If a piece is found, |
| update the variables PieceCnt, PawnCnt, Pindex and PieceList. Also |
| determine the material for each side and set the hashkey and hashbd |
| variables to represent the current board position. Array |
| PieceList[side][indx] contains the location of all the pieces of |
| either side. Array Pindex[sq] contains the indx into PieceList for a |
| given square. |
| */ |
| |
| { |
| register short i,sq; |
| epsquare = -1; |
| for (i = 0; i < 8; i++) |
| PawnCnt[white][i] = PawnCnt[black][i] = 0; |
| mtl[white] = mtl[black] = pmtl[white] = pmtl[black] = 0; |
| PieceCnt[white] = PieceCnt[black] = 0; |
| hashbd = hashkey = 0; |
| for (sq = 0; sq < 64; sq++) |
| if (color[sq] != neutral) |
| { |
| mtl[color[sq]] += value[board[sq]]; |
| if (board[sq] == pawn) |
| { |
| pmtl[color[sq]] += valueP; |
| ++PawnCnt[color[sq]][column[sq]]; |
| } |
| if (board[sq] == king) Pindex[sq] = 0; |
| else Pindex[sq] = ++PieceCnt[color[sq]]; |
| PieceList[color[sq]][Pindex[sq]] = sq; |
| hashbd ^= hashcode[color[sq]][board[sq]][sq].bd; |
| hashkey ^= hashcode[color[sq]][board[sq]][sq].key; |
| } |
| } |
| |
| |
| void pick(p1,p2) |
| short p1,p2; |
| |
| /* |
| Find the best move in the tree between indexes p1 and p2. Swap the |
| best move into the p1 element. |
| */ |
| |
| { |
| register short p,s,p0,s0; |
| struct leaf temp; |
| |
| s0 = Tree[p1].score; p0 = p1; |
| for (p = p1+1; p <= p2; p++) |
| if ((s = Tree[p].score) > s0) |
| { |
| s0 = s; p0 = p; |
| } |
| if (p0 != p1) |
| { |
| temp = Tree[p1]; Tree[p1] = Tree[p0]; Tree[p0] = temp; |
| } |
| } |
| |
| |
| void repetition(cnt) |
| short *cnt; |
| |
| /* |
| Check for draw by threefold repetition. |
| */ |
| |
| { |
| register short i,c,f,t; |
| short b[64]; |
| unsigned short m; |
| *cnt = c = 0; |
| if (GameCnt > Game50+3) |
| { |
| for (i = 0; i < 64; b[i++] = 0); |
| for (i = GameCnt; i > Game50; i--) |
| { |
| m = GameList[i].gmove; f = m>>8; t = m & 0xFF; |
| if (++b[f] == 0) c--; else c++; |
| if (--b[t] == 0) c--; else c++; |
| if (c == 0) (*cnt)++; |
| } |
| } |
| } |
| |
| |
| int SqAtakd(sq,side) |
| short sq,side; |
| |
| /* |
| See if any piece with color 'side' ataks sq. First check for pawns |
| or king, then try other pieces. Array Dcode is used to check for |
| knight attacks or R,B,Q co-linearity. |
| */ |
| |
| { |
| register short m,d,m0,m1,i,loc,piece,*PL; |
| |
| m1 = map[sq]; |
| if (side == white) m = m1-0x0F; else m = m1+0x0F; |
| if (!(m & 0x88)) |
| if (board[unmap[m]] == pawn && color[unmap[m]] == side) return(true); |
| if (side == white) m = m1-0x11; else m = m1+0x11; |
| if (!(m & 0x88)) |
| if (board[unmap[m]] == pawn && color[unmap[m]] == side) return(true); |
| if (distance(sq,PieceList[side][0]) == 1) return(true); |
| |
| PL = PieceList[side]; |
| for (i = 1; i <= PieceCnt[side]; i++) |
| { |
| loc = PL[i]; piece = board[loc]; |
| if (piece == pawn) continue; |
| m0 = map[loc]; d = Dcode[abs(m1-m0)]; |
| if (d == 0 || (Pdir[d] & pbit[piece]) == 0) continue; |
| if (piece == knight) return(true); |
| else |
| { |
| if (m1 < m0) d = -d; |
| for (m = m0+d; m != m1; m += d) |
| if (color[unmap[m]] != neutral) break; |
| if (m == m1) return(true); |
| } |
| } |
| return(false); |
| } |
| |
| |
| void ataks(side,a) |
| short side,*a; |
| |
| /* |
| Fill array atak[][] with info about ataks to a square. Bits 8-15 |
| are set if the piece (king..pawn) ataks the square. Bits 0-7 |
| contain a count of total ataks to the square. |
| */ |
| |
| { |
| register short u,m,d,c,m0; |
| short j,j1,j2,piece,i,sq,*PL; |
| |
| for (u = 0; u < 64; a[u++] = 0); |
| Dstart[pawn] = Dpwn[side]; Dstop[pawn] = Dstart[pawn] + 1; |
| PL = PieceList[side]; |
| for (i = 0; i <= PieceCnt[side]; i++) |
| { |
| sq = PL[i]; |
| m0 = map[sq]; |
| piece = board[sq]; |
| c = control[piece]; j1 = Dstart[piece]; j2 = Dstop[piece]; |
| if (sweep[piece]) |
| for (j = j1; j <= j2; j++) |
| { |
| d = Dir[j]; m = m0+d; |
| while (!(m & 0x88)) |
| { |
| u = unmap[m]; |
| a[u] = (a[u]+1) | c; |
| if (color[u] == neutral) m += d; |
| else break; |
| } |
| } |
| else |
| for (j = j1; j <= j2; j++) |
| if (!((m = m0+Dir[j]) & 0x88)) |
| { |
| u = unmap[m]; |
| a[u] = (a[u]+1) | c; |
| } |
| } |
| } |
| |
| |
| |
| void ElapsedTime(iop) |
| |
| /* |
| Determine the time that has passed since the search was started. If |
| the elapsed time exceeds the target (ResponseTime+ExtraTime) then set |
|