Created
April 21, 2018 15:25
-
-
Save lealgo/1cd588601c15422d5f1e4d41b7f1c268 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************************************************/ | |
/* micro-Max, */ | |
/* A chess program smaller than 2KB (of non-blank source), by H.G. Muller */ | |
/***************************************************************************/ | |
/* version 4.8 (~1900 characters) features: */ | |
/* - recursive negamax search */ | |
/* - all-capture quiescence search with MVV/LVA priority */ | |
/* - (internal) iterative deepening */ | |
/* - best-move-first 'sorting' */ | |
/* - a hash table storing score and best move */ | |
/* - futility pruning */ | |
/* - king safety through magnetic frozen king */ | |
/* - null-move pruning */ | |
/* - Late-move reductions */ | |
/* - full FIDE rules (expt minor promotion) and move-legality checking */ | |
/* - keep hash + rep-draw detect */ | |
/* - end-game Pawn-push bonus, new piece values, gradual promotion */ | |
/* Rehash sacrificed, simpler retrieval. Some characters squeezed out. */ | |
/* No hash-table clear, single-call move-legality checking based on K==I */ | |
/* fused to generic Winboard driver */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <signal.h> | |
#ifdef WIN32 | |
# include <windows.h> | |
#else | |
# include <sys/time.h> | |
# include <sys/times.h> | |
# include <unistd.h> | |
int GetTickCount() | |
{ struct timeval t; | |
gettimeofday(&t, NULL); | |
return t.tv_sec*1000 + t.tv_usec/1000; | |
} | |
#endif | |
int StartKey; | |
#define EMPTY 0 | |
#define WHITE 8 | |
#define BLACK 16 | |
#define STATE 64 | |
/* make unique integer from engine move representation */ | |
#define PACK_MOVE 256*K + L; | |
/* convert intger argument back to engine move representation */ | |
#define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255; | |
/* Global variables visible to engine. Normally they */ | |
/* would be replaced by the names under which these */ | |
/* are known to your engine, so that they can be */ | |
/* manipulated directly by the interface. */ | |
int Side; | |
int Move; | |
int PromPiece; | |
int Result; | |
int TimeLeft; | |
int MovesLeft; | |
int MaxDepth; | |
int Post; | |
int Fifty; | |
int UnderProm; | |
int Ticks, tlim; | |
int GameHistory[1024]; | |
char HistoryBoards[1024][STATE]; | |
int GamePtr, HistPtr; | |
#define W while | |
#define K(A,B) *(int*)(T+A+(B&8)+S*(B&7)) | |
#define J(A) K(y+A,b[y])-K(x+A,u)-K(H+A,t) | |
int U=(1<<23)-1; | |
struct _ {int K,V;char X,Y,D;} *A; /* hash table, 16M+8 entries*/ | |
int M=136,S=128,I=8e3,Q,O,K,N,j,R,J,Z; /* M=0x88 */ | |
char L, | |
w[]={0,2,2,7,-1,8,12,23}, /* relative piece values */ | |
o[]={-16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0, /* step-vector lists */ | |
7,-1,11,6,8,3,6, /* 1st dir. in o[] per piece*/ | |
6,3,5,7,4,5,3,6}, /* initial piece setup */ | |
b[129], /* board: half of 16x8+dummy*/ | |
T[1035], /* hash translation table */ | |
n[]=".?+nkbrq?*?NKBRQ"; /* piece symbols on printout*/ | |
D(k,q,l,e,E,z,n) /* recursive minimax search, k=moving side, n=depth*/ | |
int k,q,l,e,E,z,n; /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/ | |
{ /* e=score, z=prev.dest; J,Z=hashkeys; return score*/ | |
int j,r,m,v,d,h,i,F,G,P,V,f=J,g=Z,C,s; | |
char t,p,u,x,y,X,Y,H,B; | |
struct _*a=A+(J+k*E&U-1); /* lookup pos. in hash table*/ | |
q-=q<e;l-=l<=e; /* adj. window: delay bonus */ | |
d=a->D;m=a->V;X=a->X;Y=a->Y; /* resume at stored depth */ | |
if(a->K-Z|z&8| /* miss: other pos. or empty*/ | |
!(m<=q|X&8&&m>=l|X&S)) /* or window incompatible */ | |
d=Y=0; /* start iter. from scratch */ | |
X&=~M; /* start at best-move hint */ | |
W(d++<n||d<3|| /*** min depth = 2 iterative deepening loop */ | |
z==8&K==I&&(GetTickCount()-Ticks<tlim&d<=MaxDepth|| /* root: deepen upto time */ | |
(K=X,L=Y&S-9,d=3))) /* time's up: go do best */ | |
{x=B=X; /* start scan at prev. best */ | |
h=Y&S; /* request try noncastl. 1st*/ | |
P=d>2&&l+I?D(24-k,-l,1-l,-e,S,S,d-3):I; /* search null move */ | |
m=-P<l|R>35?d-2?-I:e:-P; /*** prune if > beta unconsidered:static eval */ | |
N++; /* node count (for timing) */ | |
do{u=b[x]; /* scan board looking for */ | |
if(u&k) /* own piece (inefficient!)*/ | |
{r=p=u&7; /* p = piece type (set r>0) */ | |
j=o[p+16]; /* first step vector f.piece*/ | |
W(r=p>2&r<0?-r:-o[++j]) /* loop over directions o[] */ | |
{A: /* resume normal after best */ | |
y=x;F=G=S; /* (x,y)=move, (F,G)=castl.R*/ | |
do{ /* y traverses ray, or: */ | |
H=y=h?Y^h:y+r; /* sneak in prev. best move */ | |
if(y&M)break; /* board edge hit */ | |
m=E-S&b[E]&&y-E<2&E-y<2?I:m; /* bad castling */ | |
if(p<3&y==E)H^=16; /* shift capt.sqr. H if e.p.*/ | |
t=b[H];if(t&k|p<3&!(y-x&7)-!t)break; /* capt. own, bad pawn mode */ | |
i=37*w[t&7]+(t&192); /* value of capt. piece t */ | |
if(i<0)m=I,d=98; /* K capture */ | |
if(m>=l&d>1)goto C; /* abort on fail high */ | |
v=d-1?e:i-p; /*** MVV/LVA scoring if d=1**/ | |
if(d-!t>1) /*** all captures if d=2 ***/ | |
{v=p<6?b[x+8]-b[y+8]:0; /* center positional pts. */ | |
b[G]=b[H]=b[x]=0;b[y]=u|32; /* do move, set non-virgin */ | |
if(!(G&M))b[F]=k+6,v+=50; /* castling: put R & score */ | |
v-=p-4|R>30?0:20; /*** freeze K in mid-game ***/ | |
if(p<3) /* pawns: */ | |
{v-=9*((x-2&M||b[x-2]-u)+ /* structure, undefended */ | |
(x+2&M||b[x+2]-u)-1 /* squares plus bias */ | |
+(b[x^16]==k+36)) /*** cling to magnetic K ***/ | |
-(R>>2); /* end-game Pawn-push bonus */ | |
i+=V=y+r+1&S?647-p:2*(u&y+16&32); /* promotion / passer bonus */ | |
b[y]+=V; /* upgrade P or convert to Q*/ | |
} | |
J+=J(0);Z+=J(8)+G-S; | |
v+=e+i;V=m>q?m:q; /*** new eval & alpha ****/ | |
C=d-1-(d>5&p>2&!t&!h); /* nw depth, reduce non-cpt.*/ | |
C=R>30|P-I|d<3||t&&p-4?C:d; /* extend 1 ply if in-check */ | |
do | |
s=C>2|v>V?-D(24-k,-l,-V,-v,/*** futility, recursive eval. of reply */ | |
F,y,C):v; | |
W(s>q&++C<d); v=s; /* no fail:re-srch unreduced*/ | |
if(z&8&&K-I) /* move pending: check legal*/ | |
{if(v+I&&x==K&y==L) /* if move found */ | |
{Q=-e-i;O=F; | |
a->D=99;a->V=0; /* lock game in hash as draw*/ | |
R+=i>>7; /*** total captd material ***/ | |
if((b[y]&7)!=p && PromPiece == 'n') UnderProm = y; | |
if((b[y]&7)!=p) {printf("tellics kibitz promotion\n");fflush(stdout);}; | |
Fifty = t|p<3?0:Fifty+1; | |
return l;} /* & not in check, signal */ | |
v=m; /* (prevent fail-lows on */ | |
} /* K-capt. replies) */ | |
J=f;Z=g; | |
b[G]=k+6;b[F]=b[y]=0;b[x]=u;b[H]=t; /* undo move,G can be dummy */ | |
} /* if non-castling */ | |
if(v>m) /* new best, update max,best*/ | |
m=v,X=x,Y=y|S&F; /* mark non-double with S */ | |
if(h){h=0;goto A;} /* redo after doing old best*/ | |
if(x+r-y|u&32| /* not 1st step,moved before*/ | |
p>2&(p-4|j-7|| /* no P & no lateral K move,*/ | |
b[G=x+3^r>>1&7]-k-6 /* no virgin R in corner G, */ | |
||b[G^1]|b[G^2]) /* no 2 empty sq. next to R */ | |
)t+=p<5; /* fake capt. for nonsliding*/ | |
else F=y; /* enable e.p. */ | |
}W(!t); /* if not capt. continue ray*/ | |
}}}W((x=x+9&~M)-B); /* next sqr. of board, wrap */ | |
C:m=m+I|P==I?m:0; /*** check test thru NM best loses K: (stale)mate*/ | |
if(a->D<99) /* protect game history */ | |
a->K=Z,a->V=m,a->D=d, /* always store in hash tab */ | |
a->X=X|8*(m>q)|S*(m<l),a->Y=Y; /* move, type (bound/exact),*/ | |
if(z==8&Post){ | |
printf("%2d ",d-2); | |
printf("%6d ",m); | |
printf("%8d %10d %c%c%c%c\n",(GetTickCount()-Ticks)/10,N, | |
'a'+(X&7),'8'-(X>>4),'a'+(Y&7),'8'-(Y>>4&7)),fflush(stdout);} | |
} /* encoded in X S,8 bits */ | |
if(z==S+1)K=X,L=Y&~M; | |
return m+=m<e; /* delayed-loss bonus */ | |
} | |
void InitEngine() | |
{ | |
int j; | |
K=8;W(K--) | |
{L=8;W(L--)b[16*L+K+8]=(K-4)*(K-4)+(L-3.5)*(L-3.5); /* center-pts table */ | |
} /*(in unused half b[])*/ | |
N=1035;W(N-->M)T[N]=rand()>>9; | |
} | |
void InitGame() | |
{ | |
int i,j; | |
for(i=0;i<128;i++)b[i&~M]=0; | |
K=8;W(K--) | |
{b[K]=(b[K+112]=o[K+24]+8)+8;b[K+16]=18;b[K+96]=9; /* initial board setup*/ | |
} /*(in unused half b[])*/ | |
Side = WHITE; Q=0; O=S; | |
Fifty = R = 0; | |
UnderProm = -1; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
char moves[] = "a2a3a7a6b2b3b7b6c2c3c7c6d2d3d7d6e2e3e7e6f2f3f7f6g2g3g7g6h2h3h7h6a3a4a6a5b3b4b6b5c3c4c6c5d3d4d6d5e3e4e6e5f3f4f6f5g3g4g6g5h3h4h6h5"; | |
int Computer, MaxTime, MaxMoves, TimeInc, sec, i; | |
char line[256], command[256]; | |
int m, nr; | |
if (argc > 1 && sscanf(argv[1], "%d", &m) == 1) | |
U = (1 << m) - 1; | |
A = (struct _ *)calloc(U + 1, sizeof(struct _)); | |
printf("hash: %d\n", U); | |
InitEngine(); | |
InitGame(); | |
Computer = EMPTY; | |
MaxTime = 10000; /* 10 sec */ | |
MaxDepth = 30; /* maximum depth of your search */ | |
for (char *c = moves; c[0]; c+=4) | |
{ | |
m = c[0]<'a' | c[0]> 'h' | c[1]<'1' | c[1]> '8' | | |
c[2]<'a' | c[2]> 'h' | c[3]<'1' | c[3]> '8'; | |
PromPiece = 0; | |
{ | |
K = c[0] - 16 * c[1] + 799; | |
L = c[2] - 16 * c[3] + 799; | |
} | |
int millis = GetTickCount(); | |
if (m) | |
/* doesn't have move syntax */ | |
printf("Error (unknown command): %s\n", command); | |
else if (D(Side, -I, I, Q, O, 8, 3) != I) | |
{ | |
/* did have move syntax, but illegal move */ | |
printf("Illegal move:%s\n", line); | |
} | |
else | |
{ /* legal move, perform it */ | |
printf("%c%c%c%c: %d\n", c[0], c[1], c[2], c[3], GetTickCount()-millis); | |
Side ^= 24; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment