Are you familiar with the Pascal programming language, or its relations Modula-2, Oberon, or Delphi? Are you interested in trying out a new chess program source? Do you have a machine built in the past ten years that can run one of the several inexpensive (or free) Pascal compilers?
Well, I have something just for you! And it won't cost a penny, only a bit of your time.
About thirty five years ago, David Slate, the co-author of the Northwestern Chess 4.x program, wrote a demonstration chess program in Pascal which was published in Byte magazine. Named "Chess 0.5" (clever, eh?), the program was written in the Zurich ETH dialect of Pascal for the CDC 6000 series mainframes. This was fine if you happened to have access to one of these monsters, but of limited use otherwise. Still, it was a decent attempt at an educational resource for Pascal and for chess programming. A little hunting will locate this very source on the web.
Since that time, there haven't been too many Pascal chess program sources publicly available. Also, Pascal has evolved over the years, and the dialect employed by Slate has long since gone out of fashion.
I remember when Chess 0.5 was published (See _The Byte Book of Pascal_ which includes a reprint of the articles and source) and I think it's time for a replacement. To bring this about, I've started the Bozochess Project (silly name is silly and will be changed). This is a new bitboard chess program I've started using a modern, more standard Pascal dialect. More portable, too; I'm using the Free Pascal compiler but I'm trying to avoid language features which might be unsupported in other modern Pascal variants.
How else is Bozochess different from Chess 0.5?
1) Bozochess is much more object oriented. Although it does not use Pascal classes (but this may change), it could be easily converted.
2) Bozochess doesn't need a US$5,000,000 computer to run. It might even be possible to run it on a 64 KB RAM microcomputer if some features were removed.
3) Bozochess supports the modern chess data interchange formats (SAN, FEN, EPD, PGN); it does not support the EDN (English Descriptive Notation) used in Chess 0.5.
4) Bozochess routine names are not constrained to the six character limit from the Old Days Pascal. There has been considerable effort to use meaningful and somewhat long names which can be decoded without help from a cheat sheet.
5) There have been several optimizations of bitboard and bitboard database processing. The same is true of move generation, pin detection, and other aspects of the program.
6) There is not a single damn "goto" in the entire program. This ain't Fortran.
7) There is not a single damn variant field list in the entire program. No machine dependency kludges here.
8) The poor string processing of the Old Days Pascal is gone. Exactly how this will be replaced is still uncertain, but might use the ansistring type seen in Free Pascal.
9) Bozochess is not multi-threaded, but is being designed for such.
10) Bozochess plays full legal chess; specifically, it understands all draw conditions and not just stalemate.
11) Bozochess will have a provision for a transposition subsystem, an opening book; maybe tablebases as well.
12) Bozochess is written using the full ASCII character glyph set and not just the six bit display code from the Old Days.
The Bozochess source, a single file, is now about 2,300 lines long. The move generation is not yet complete, but I expect to have it along with a perft routine running in the next week or two. Once Bozochess gets this far, I'll make the source available via email for anyone who would like to take a look and hopefully can make some constructive suggestions. Watch this thread for progress reports.
Sample code (working):
Code: Select all
function bbcount(var bb: bbtype): integer;
var
sum: integer;
bbwindex: integer;
begin
sum := 0;
for bbwindex := 0 to bbwindexlimit do
sum := sum + bitcountvec[bb[bbwindex]];
bbcount := sum;
end; { bbcount }
function bbfirstsq(var bb: bbtype): sqxtype;
var
firstsq: sqxtype;
bbwindex: integer;
wordfirstbit: integer;
begin
firstsq := sqnil;
bbwindex := 0;
while (firstsq = sqnil) and (bbwindex <= bbwindexlimit) do
begin
wordfirstbit := bitfirstvec[bb[bbwindex]];
if wordfirstbit >= 0 then
firstsq := sqxtype(wordfirstbit + (bbwindex * bbwbitlen))
else
bbwindex := bbwindex + 1;
end;
bbfirstsq := firstsq;
end; { bbfirstsq }
Code: Select all
procedure bbdbaddattacks(var bbdb: bbdbtype; sq: sqtype; man: manrtype);
var
color: colortype;
dir: dirtype;
atkfrbb: bbtype;
atkfrsq: sqxtype;
dir0, dir1: dirtype;
scansq: sqxtype;
stopped: boolean;
begin
with bbdb do
begin
color := mantocolor[man];
{ Generate the attacks-from-square bitboard }
case mantopiece[man] of
pp: atkfrbb := pawnattackbbvec[color, sq];
pn: atkfrbb := knightattackbbvec[sq];
pb, pr, pq:
begin
bbreset(atkfrbb);
dir0 := mantodir0[man];
dir1 := mantodir1[man];
for dir := dir0 to dir1 do
begin
scansq := sq;
stopped := false;
while not stopped do
begin
scansq := sqdirtonextsq[scansq, dir];
if scansq = sqnil then
stopped := true
else
begin
bbsetsq(atkfrbb, scansq);
if bbtestsq(merge, scansq) then
stopped := true;
end;
end;
end;
end;
pk: atkfrbb := kingattackbbvec[sq];
end;
{ Apply the attacks-from-square bitboard }
atkfs[sq] := atkfrbb;
bbior2d(atkbc[color], atkfrbb);
repeat
atkfrsq := bbnextsq(atkfrbb);
if atkfrsq <> sqnil then
bbsetsq(atkts[atkfrsq], sq);
until atkfrsq = sqnil;
end;
end; { bbdbaddattacks }
procedure bbdbdelattacks(var bbdb: bbdbtype; sq: sqtype; man: manrtype);
var
color: colortype;
atkfrbb: bbtype;
atkfrsq: sqxtype;
begin
with bbdb do
begin
color := mantocolor[man];
atkfrbb := atkfs[sq];
bbreset(atkfs[sq]);
repeat
atkfrsq := bbnextsq(atkfrbb);
if atkfrsq <> sqnil then
begin
bbresetsq(atkts[atkfrsq], sq);
if bbni2(locbc[color], atkts[atkfrsq]) then
bbresetsq(atkbc[color], atkfrsq);
end;
until atkfrsq = sqnil;
end;
end; { bbdbdelattacks }
procedure bbdbproattacks(var bbdb: bbdbtype; sq: sqtype);
var
atktobb: bbtype;
atktosq: sqxtype;
color: colortype;
dir: dirtype;
scansq: sqxtype;
stopped: boolean;
begin
with bbdb do
begin
atktobb := atkts[sq];
repeat
atktosq := bbnextsq(atktobb);
if atktosq <> sqnil then
if bbtestsq(sweep, atktosq) then
begin
color := bbdbsqcolor(bbdb, atktosq);
dir := sqsqtodir[atktosq, sq];
scansq := sq;
stopped := false;
while not stopped do
begin
scansq := sqdirtonextsq[scansq, dir];
if scansq = sqnil then
stopped := true
else
begin
bbsetsq(atkfs[atktosq], scansq);
bbsetsq(atkts[scansq], atktosq);
bbsetsq(atkbc[color], scansq);
if bbtestsq(merge, scansq) then
stopped := true;
end;
end;
end;
until atktosq = sqnil;
end;
end; { bbdbproattacks }
procedure bbdbcutattacks(var bbdb: bbdbtype; sq: sqtype);
var
atktobb: bbtype;
atktosq: sqxtype;
color: colortype;
dir: dirtype;
scansq: sqxtype;
stopped: boolean;
begin
with bbdb do
begin
atktobb := atkts[sq];
repeat
atktosq := bbnextsq(atktobb);
if atktosq <> sqnil then
if bbtestsq(sweep, atktosq) then
begin
color := bbdbsqcolor(bbdb, atktosq);
dir := sqsqtodir[atktosq, sq];
scansq := sq;
stopped := false;
while not stopped do
begin
scansq := sqdirtonextsq[scansq, dir];
if scansq = sqnil then
stopped := true
else
begin
bbresetsq(atkfs[atktosq], scansq);
bbresetsq(atkts[scansq], atktosq);
if bbni2(locbc[color], atkts[scansq]) then
bbresetsq(atkbc[color], scansq);
if bbtestsq(merge, scansq) then
stopped := true;
end;
end;
end;
until atktosq = sqnil;
end;
end; { bbdbcutattacks }