Skip to content

Instantly share code, notes, and snippets.

@lealgo
Created April 21, 2018 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lealgo/1cd588601c15422d5f1e4d41b7f1c268 to your computer and use it in GitHub Desktop.
Save lealgo/1cd588601c15422d5f1e4d41b7f1c268 to your computer and use it in GitHub Desktop.
/***************************************************************************/
/* 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