Bozochess status 2011.10.08
There's enough working code present to get some early timing results. Running on a rather dated 32 bit 3 GHz Pentium 4 machine, Bozo can perform a complete move make/unmake cycle in just under 4.5 microseconds. Each cycle includes a full bitboard database update, a color/man census update, check/double check status update, and a few other items. At present, there are no significant attempts at optimization and no assembly language.
I'm sticking with the Free Pascal dialect with all settings at their defaults and no source directives of any kind. Specifically, there are no directives that select one of the several "other" dialects and their corresponding features. As mentioned earlier, I want to avoid dependencies as much as possible; I will leave the heroic attempts at the cycle counting level optimizations to the end users.
Actually, much of the code would run on the original Pascal dialect of the mid 1970s. The huge exception is my use of the "@" dereference operator. This is needed almost entirely for specifying an actual parameter for an object method that expects a pointer to the object of interest. It would be possible to avoid use of the dereference operator, but it would complicate the code somewhat and add nothing to the final results.
The only global variables in the program are those which are initialized to constant values when the program starts.
An interesting feature of Bozo is its "postype" chess position record type. This structure contains a linked list of prior position environment values records. Upon move execution, values like the played move, castling availability, the en passant target square and the half move counter are aptured on a value record and appended to the tail of the list. Upon retraction, the tail element is detached and its values are used to revert the position to its prior state. This means that while the call to "posexecute" needs a move parameter, the corresponding call to "posretract" does not as the move is popped off the position's internal list. This approach also makes repetition draw detection easier and without the need for a globally visible, non-thread safe stack.
Currently, I'm not using any class/object Pascal features in part to avoid dialect dependencies. However, each of nearly all the structures (i.e., Pascal record types) have their own set of methods and each of these motheds has as its first formal parameter a pointer to the object of interes. For some of thes structures, there are even constructor functions and destructor routines.
Some structures:
boardtype: an array of the contents of the 64 squares.
bbtype: a bitboard.
bbdbtype: a bitboard database.
movetype: a move.
gms: generated move set (all moves legal)
census: a 2-D color/man counter array
spevtype: a record for saved position environment values
postype: a general chess position
I hope to release a debugged perft in a few weks.
Announcement: The Bozochess Project
Moderators: hgm, Rebel, chrisw
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
The Bozochess SAN encoder
The Bozochess SAN encoder:
Code: Select all
procedure moveencode(moveptr: moveptrtype; sanptr: sanptrtype);
var
index: integer;
procedure addch(ch: char);
begin
sanptr^[index] := ch; inc(index);
end { addch };
procedure addbfile(bfile: bfiletype);
begin
addch(bfiletochar[bfile]);
end { addbfile };
procedure addbrank(brank: branktype);
begin
addch(branktochar[brank]);
end { addbrank };
procedure addsq(sq: sqtype);
begin
addbfile(mapsqtobfile(sq)); addbrank(mapsqtobrank(sq));
end { addsq };
procedure addpiece(piece: piecetype);
begin
addch(piecetoucchar[piece]);
end; { addpiece }
begin
with moveptr^ do
begin
index := 0;
if moveflagtest(moveptr, mfnull) or moveflagtest(moveptr, mfvoid) then
if moveflagtest(moveptr, mfnull) then
begin
addch('<'); addch('n'); addch('u'); addch('l'); addch('l'); addch('>');
end
else
begin
addch('<'); addch('v'); addch('o'); addch('i'); addch('d'); addch('>');
end
else
begin
if moveflagtest(moveptr, mfbust) then addch('*');
case msc of
mscreg:
begin
if mantopiece[frman] = piecep then
begin
if toman <> manvv then begin addbfile(mapsqtobfile(frsq)); addch('x') end;
addsq(tosq);
end
else
begin
addpiece(mantopiece[frman]);
if moveflagtest(moveptr, mfandf) then addbfile(mapsqtobfile(frsq));
if moveflagtest(moveptr, mfandr) then addbrank(mapsqtobrank(frsq));
if toman <> manvv then addch('x');
addsq(tosq);
end;
end;
mscepc:
begin
addbfile(mapsqtobfile(frsq)); addch('x'); addsq(tosq);
end;
msccqs:
begin
addch('O'); addch('-'); addch('O'); addch('-'); addch('O');
end;
msccks:
begin
addch('O'); addch('-'); addch('O');
end;
mscppn, mscppb, mscppr, mscppq:
begin
if toman <> manvv then begin addbfile(mapsqtobfile(frsq)); addch('x') end;
addsq(tosq); addch('='); addpiece(msctopiece[msc]);
end;
end;
if moveflagtest(moveptr, mfchck) then
if moveflagtest(moveptr, mfchmt) then addch('#') else addch('+');
end;
addch(char(0));
end;
end; { moveencode }
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Announcement: The Bozochess Project
I thought about naming the program after a different clown; i.e., Pennywise Chess. But a certain author who lives an hour's drive away from me might come over and kick my butt. And that's after he's already used my name for a character in one of his novels!
Once the program can play a full game and be built under two different compilers, I'll rename it.
At the moment, I'm removing superfluous semicolons from those routines unlikely to see further change. After that, it's pin/frozen bitboard generation, the en passant and castling routines, move generation (nocheck+evasion), fast move counting, perft, fast mate detection, fast checking move marking, fast checking move generation, and gainer move generation. The code is in my head but not in the machine.
Once the program can play a full game and be built under two different compilers, I'll rename it.
At the moment, I'm removing superfluous semicolons from those routines unlikely to see further change. After that, it's pin/frozen bitboard generation, the en passant and castling routines, move generation (nocheck+evasion), fast move counting, perft, fast mate detection, fast checking move marking, fast checking move generation, and gainer move generation. The code is in my head but not in the machine.
Code: Select all
$ wc bozochess.pas
2797 9541 82161 bozochess.pas
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Announcement: The Bozochess Project
Actually, the perft code is in place. But it can't do much until the move generation and the move counting is implemented.
Code: Select all
function posperftbulk(posptr: posptrtype; ply, depth: integer): nodecounttype;
var
result, subsum: nodecounttype;
newply, newdepth: integer;
gms: gmstype;
index: integer;
begin
if depth = 0 then result := 1
else
if depth = 1 then result := poscount(posptr)
else
with gms do
begin
result := 0; newply := ply + 1; newdepth := depth + 1; posgenerate(posptr, @gms);
if ply = 0 then begin posassignallnotationflags(posptr, @gms); gmssortbysan(@gms) end;
for index := 0 to movecount - 1 do
begin
posexecute(posptr, @moves[index]);
subsum := posperftbulk(posptr, newply, newdepth);
posretract(posptr);
if ply = 0 then begin write(' '); movewrite(@moves[index]); writeln(' ', subsum) end;
result := result + subsum
end
end;
posperftbulk := result
end; { posperftbulk }
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Not-quite-perft
It looks like Bozo has the beginnings of a working perft. Alas:
1) No check evasion (but check/double check detection works)
2) No en passant (but promotions look okay)
3) No castling
4) No bulk counting
5) No transposition assistance
6) No sorted by SAN output
7) No user input interface
8) No FEN decoding
9) No input position legality checking
So for now the program can manage only (1, 20, 400, 8902).
As with my other programs, the move generator produces only legal moves and so some extra care is needed with its construction.
The execute/retract routines are working with one exception: the en passant target square may be non null even if no en passant capture is possible due to a pin. This will be fixed soon as it has a definite effect on move generation.
The move execute routine {note the"TBD" comment):
And the simpler move retract routine:
1) No check evasion (but check/double check detection works)
2) No en passant (but promotions look okay)
3) No castling
4) No bulk counting
5) No transposition assistance
6) No sorted by SAN output
7) No user input interface
8) No FEN decoding
9) No input position legality checking
So for now the program can manage only (1, 20, 400, 8902).
As with my other programs, the move generator produces only legal moves and so some extra care is needed with its construction.
The execute/retract routines are working with one exception: the en passant target square may be non null even if no en passant capture is possible due to a pin. This will be fixed soon as it has a definite effect on move generation.
The move execute routine {note the"TBD" comment):
Code: Select all
procedure posexecute(posptr: posptrtype; moveptr: moveptrtype);
var
spevnodeptr: spevnodeptrtype;
castle: castletype;
isnotnull: boolean;
begin
with posptr^ do
begin
{ Create and load a saved position environment values node }
spevnodeptr := spevnodenew();
with spevnodeptr^ do
begin
spev.move := moveptr^;
spev.good := good; spev.evil := evil;
spev.cvas := cvas; spev.epsq := epsq; spev.hmvc := hmvc; spev.fmvn := fmvn;
spev.inch := inch; spev.dbch := dbch; spev.pmbb := pmbb; spev.fmbb := fmbb;
spev.mphc := mphc
end;
{ Append the saved position environment values node }
if spevnodetail = nil then spevnodehead := spevnodeptr
else
begin
spevnodetail^.next := spevnodeptr; spevnodeptr^.prev := spevnodetail
end;
spevnodetail := spevnodeptr;
{ Make the move }
with moveptr^ do
begin
{ Perform forward motion only for a non-null move }
isnotnull := not moveflagtest(moveptr, mfnull);
if isnotnull then
begin
case msc of
mscreg:
begin
if toman <> manvv then posdelman(posptr, toman, tosq);
posmovman(posptr, frman, frsq, tosq)
end;
mscepc:
begin
posdelman(
posptr,
colorpiecetoman[evil, piecep],
sqdirtonextsq[tosq, pawnadvdir[evil]]);
posmovman(posptr, frman, frsq, tosq)
end;
msccqs, msccks:
begin
castle := mapcolormsctocastle(good, msc);
posmovman(posptr, frman, frsq, tosq);
posmovman(
posptr,
castletorook[castle],
castlerook0sq[castle],
castlerook1sq[castle])
end;
mscppn, mscppb, mscppr, mscppq:
begin
if toman <> manvv then posdelman(posptr, toman, tosq);
posdelman(posptr, frman, frsq);
posaddman(posptr, colorpiecetoman[good, msctopiece[msc]], tosq)
end
end
end;
{ Various updates }
good := othercolor[good]; evil := othercolor[evil];
if isnotnull then
if (cvas <> 0) then
for castle := castlewq to castlebk do
if odd(cvas shr castle) then
if (frsq = castleking0sq[castle]) or
(frsq = castlerook0sq[castle]) or (tosq = castlerook0sq[castle]) then
begin
cvas := cvas and (not (1 shl castle));
hashxor2d(@mphc, @hashcastlevec[castle])
end;
if epsq <> sqnil then
begin
hashxor2d(@mphc, @hashepfilevec[mapsqtobfile(epsq)]); epsq := sqnil
end;
if isnotnull then
if (mantopiece[frman] = piecep) and ((frsq xor tosq) = (bfilelen * 2)) then
begin
epsq := sqdirtonextsq[frsq, pawnadvdir[evil]];
{TBD}
if epsq <> sqnil then hashxor2d(@mphc, @hashepfilevec[mapsqtobfile(epsq)])
end;
if isnotnull then
if (toman <> manvv) or (mantopiece[frman] = piecep) then hmvc := 0 else inc(hmvc);
if good = colorw then inc(fmvn);
posrebuild(posptr)
end
end
end; { posexecute }
Code: Select all
procedure posretract(posptr: posptrtype);
var
move: movetype;
spevnodeptr: spevnodeptrtype;
castle: castletype;
isnotnull: boolean;
begin
with posptr^ do
begin
{ Detach the saved position environment values node }
spevnodeptr := spevnodetail; spevnodetail := spevnodetail^.prev;
if spevnodetail = nil then spevnodehead := nil else spevnodetail^.next := nil;
{ Unload and destroy the saved position environment values node }
with spevnodeptr^ do
begin
move := spev.move;
good := spev.good; evil := spev.evil;
cvas := spev.cvas; epsq := spev.epsq; hmvc := spev.hmvc; fmvn := spev.fmvn;
inch := spev.inch; dbch := spev.dbch; pmbb := spev.pmbb; fmbb := spev.fmbb
end;
spevnodedispose(spevnodeptr);
{ Unmake the move }
with move do
begin
{ Perform backward motion only for a non-null move }
isnotnull := not moveflagtest(@move, mfnull);
if isnotnull then
begin
case msc of
mscreg:
begin
posmovman(posptr, frman, tosq, frsq);
if toman <> manvv then posaddman(posptr, toman, tosq)
end;
mscepc:
begin
posmovman(posptr, frman, tosq, frsq);
posaddman(
posptr,
colorpiecetoman[evil, piecep],
sqdirtonextsq[tosq, pawnadvdir[evil]])
end;
msccqs, msccks:
begin
castle := mapcolormsctocastle(good, msc);
posmovman(
posptr,
castletorook[castle],
castlerook1sq[castle],
castlerook0sq[castle]);
posmovman(posptr, frman, tosq, frsq)
end;
mscppn, mscppb, mscppr, mscppq:
begin
posdelman(posptr, colorpiecetoman[good, msctopiece[msc]], tosq);
posaddman(posptr, frman, frsq);
if toman <> manvv then posaddman(posptr, toman, tosq)
end;
end
end
end
end
end; { posretract }
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Announcement: The Bozochess Project
Truth: The first draft of any chess program has at least three en passant bugs.
The code for updating check status and pinned/frozen men:
The code for updating check status and pinned/frozen men:
Code: Select all
procedure posrebuild(posptr: posptrtype);
procedure posrebuildcheckstatuses;
var
goodkingsq: sqxtype;
atkbb: bbtype;
begin
with posptr^, bbdb do
begin
goodkingsq := ksqv[good];
if goodkingsq = sqnil then begin inch := false; dbch := false end
else
begin
inch := bbtestsq(@atkbc[evil], goodkingsq);
if inch then
begin
bband2(@atkbb, @locbc[evil], @atkts[goodkingsq]);
dbch := bbcount(@atkbb) > 1
end
else
dbch := false
end
end
end; { posrebuildcheckstatuses }
procedure posrebuildpindbitboards;
var
color0, color1: colortype;
king0sq: sqxtype;
sweep1bb: bbtype;
cand0sq: sqxtype;
cand0bb: bbtype;
dir: dirtype;
deltar: integer;
begin
with posptr^, bbdb do
begin
{ Initialize the pinned man and frozen man bitboard results }
bbreset(@pmbb); bbreset(@fmbb);
{ Loop through each color to locate pinned men }
for color0 := colorw to colorb do
begin
{ Get the king square of the first color and make sure it exists }
king0sq := ksqv[color0];
if king0sq <> sqnil then
begin
{ Get the second color and check if there are any second color sweep men }
color1 := othercolor[color0];
bband2(@sweep1bb, @sweep, @locbc[color1]);
if not bbisreset(@sweep1bb) then
begin
{ Build a bitboard of candidate pinned men for the first color }
bband3(@cand0bb, @locbc[color0], @sweepraybbvec[king0sq], @atkbc[color1]);
{ Loop through each first color candidate }
repeat
cand0sq := bbnextsq(@cand0bb);
if cand0sq <> sqnil then
{ Check for no men between the king and the candidate }
if bbni2(@merge, @pathwaybbvec[king0sq, cand0sq]) then
begin
{ Calculate the king/candidate direction }
dir := sqsqtodir[king0sq, cand0sq];
{ Check if a real pin exists }
if not bbni3(@openraybbvec[cand0sq, dir], @sweep1bb, @atkts[cand0sq]) then
begin
{ A pinned man was found; add to pinned man result }
bbsetsq(@pmbb, cand0sq);
{ Check for frozen status of the pinned man }
case mantopiece[board.sqv[cand0sq]] of
piecep:
begin
deltar := calcbrankdelta(king0sq, cand0sq);
if isdirortho(dir) then
begin if deltar = 0 then bbsetsq(@fmbb, cand0sq) end
else
begin
if color0 = colorw then
begin if deltar < 0 then bbsetsq(@fmbb, cand0sq) end
else
begin if deltar > 0 then bbsetsq(@fmbb, cand0sq) end
end
end;
piecen: bbsetsq(@fmbb, cand0sq);
pieceb: if isdirortho(dir) then bbsetsq(@fmbb, cand0sq);
piecer: if isdirdiago(dir) then bbsetsq(@fmbb, cand0sq);
pieceq: ;
end
end
end
until cand0sq = sqnil;
end
end
end
end
end; { posrebuildpindbitboards }
begin
posrebuildcheckstatuses; posrebuildpindbitboards
end; { posrebuild }
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Not-quite-perft
The first attempt at check evasion has been installed, so the program can now make it to (1, 20, 400, 8902, 197281).sje wrote:It looks like Bozo has the beginnings of a working perft. Alas:
1) No check evasion (but check/double check detection works)
2) No en passant (but promotions look okay)
3) No castling
4) No bulk counting
5) No transposition assistance
6) No sorted by SAN output
7) No user input interface
8) No FEN decoding
9) No input position legality checking
So for now the program can manage only (1, 20, 400, 8902).
The SAN move sorter is complete. The move execute and retract routines have been instrumented with sanity checks. The program can run without problems with compiler-supplied range checking activated.
Next on the list: en passant, then castling.
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Not-quite-perft
With the en passant code in place, perft is now returning 1, 20 , 400, 8902, 197281, 4865609, 119060324, 3195901860, and maybe more if I let it run long enough. Castling is also in place.
Still, the program is MISSING:
1) Bulk counting without generation
2) High speed mate detection
3) Repetition detection (but the hash code all seems to work)
4) Transposition tables
5) FEN decode and legality checking
6) User interface
7) EPD encoding/decoding
8) SAN full notation marking and decoding (sorting works)
9) Many utility routines (I/O, timing, option parsing, etc.)
The above should be in place before the initial release; also maybe a few more things like random game generation.
Current source size:
Extra care has been taken in several areas:
1) Only legal moves are output from the move generation routines. There is no need to test for passive king-in-check status, although there is some easily removed sanity checking code in place for this.
2) An en passant target square is generated if and only if there is at least one legal en passant capture.
3) Castling is totally table driven so that chess960/FRC can be easily implemented. Although I'm not a fan of Chess960, others may find this feature of interest.
4) Null moves are handled properly although this won't be of use until a search is installed.
5) Game termination status determination priority is in place: checkmate/stalemate, fifty move, insufficient, repetition.
Still, the program is MISSING:
1) Bulk counting without generation
2) High speed mate detection
3) Repetition detection (but the hash code all seems to work)
4) Transposition tables
5) FEN decode and legality checking
6) User interface
7) EPD encoding/decoding
8) SAN full notation marking and decoding (sorting works)
9) Many utility routines (I/O, timing, option parsing, etc.)
The above should be in place before the initial release; also maybe a few more things like random game generation.
Current source size:
Code: Select all
sje@clare:~/Projects/bozochess$ wc -l bozochess.pas
3521 bozochess.pas
1) Only legal moves are output from the move generation routines. There is no need to test for passive king-in-check status, although there is some easily removed sanity checking code in place for this.
2) An en passant target square is generated if and only if there is at least one legal en passant capture.
3) Castling is totally table driven so that chess960/FRC can be easily implemented. Although I'm not a fan of Chess960, others may find this feature of interest.
4) Null moves are handled properly although this won't be of use until a search is installed.
5) Game termination status determination priority is in place: checkmate/stalemate, fifty move, insufficient, repetition.
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
The basic move generation
The basic move generation is in place and is unlikely to change very much. Here it is:
Code: Select all
procedure posgenerate(var pos: postype; var gms: gmstype);
procedure posgenerateevasion;
var
goodkingsq: sqtype;
goodpawn, goodking: manrtype;
goodbb, goodpawnbb: bbtype;
r2brank, r4brank, r8brank: branktype;
advdir: dirtype;
singlecheck: boolean;
checkerbb: bbtype;
checkersq: sqxtype;
checkerman: mantype;
checkeropr: boolean;
flightbb: bbtype;
flightsq: sqxtype;
atkbb: bbtype;
atksq: sqxtype;
defbb: bbtype;
defsq: sqxtype;
defman: manrtype;
shadowsq: sqxtype;
cvpwbb: bbtype;
pathbb: bbtype;
pathsq: sqxtype;
ipdbb: bbtype;
ipdsq: sqxtype;
epmove: movetype;
begin
with pos, board, bbdb do
begin
{ Set various local constant values }
goodkingsq := ksqv[good];
goodpawn := synthpawn[good]; goodking := synthking[good];
goodbb := locbc[good]; goodpawnbb := locbm[goodpawn];
r2brank := normalbrank[good, brank2];
r4brank := normalbrank[good, brank4];
r8brank := normalbrank[good, brank8];
advdir := pawnadvdir[good];
singlecheck := not dbch;
bband2(checkerbb, locbc[evil], atkts[goodkingsq]);
{ Set various checker data local constant values according to double check status }
if singlecheck then
begin
checkersq := bbfirstsq(checkerbb); checkerman := sqv[checkersq];
checkeropr := mapsqtobrank(checkersq) = r8brank
end
else
begin
checkersq := sqnil; checkerman := manvv; checkeropr := false
end;
{ Initialize the king flight squares bitboard }
flightbb := kingatkbbvec[goodkingsq];
bband2c2d(flightbb, goodbb); bband2c2d(flightbb, atkbc[evil]);
{ Remove any shadow squares from the flight bitboard }
atkbb := checkerbb;
repeat
atksq := bbnextsq(atkbb);
if (atksq <> sqnil) then
if bbtestsq(sweep, atksq) then
begin
shadowsq := shadowsqvec[atksq, goodkingsq];
if shadowsq <> sqnil then bbresetsq(flightbb, shadowsq)
end
until atksq = sqnil;
{ Attempt non en passant capture of a singleton attacker (no king moves, no en passant) }
if singlecheck then
begin
bband2(defbb, atkts[checkersq], goodbb); bband2c2d(defbb, pmbb);
bbresetsq(defbb, goodkingsq);
repeat
defsq := bbnextsq(defbb);
if defsq <> sqnil then
begin
defman := sqv[defsq];
if (defman <> goodpawn) or (not checkeropr) then
gmspushm2(gms, defsq, checkersq, defman, checkerman)
else
gmspushpscapt(gms, defsq, checkersq, defman, checkerman)
end
until defsq = sqnil
end;
{ Attempt king capture of a single attacker }
if singlecheck then
if bbtestsq(flightbb, checkersq) then
begin
gmspushm2(gms, goodkingsq, checkersq, goodking, checkerman);
bbresetsq(flightbb, checkersq)
end;
{ Attempt remaining king moves }
repeat
flightsq := bbnextsq(flightbb);
if flightsq <> sqnil then
gmspushm2(gms, goodkingsq, flightsq, goodking, sqv[flightsq])
until flightsq = sqnil;
{ Attempt interposition against a singleton sweep attacker (all non captures) }
if singlecheck then
if bbtestsq(sweep, checkersq) then
if not bbtestsq(atkfs[goodkingsq], checkersq) then
begin
cvpwbb := pathwaybbvec[goodkingsq, checkersq];
{ Try non pawn interpositions }
pathbb := cvpwbb;
repeat
pathsq := bbnextsq(pathbb);
if pathsq <> sqnil then
begin
bband2(ipdbb, atkts[pathsq], goodbb);
bband2c2d(ipdbb, goodpawnbb); bband2c2d(ipdbb, pmbb);
bbresetsq(ipdbb, goodkingsq);
repeat
ipdsq := bbnextsq(ipdbb);
if ipdsq <> sqnil then
gmspushm1(gms, ipdsq, pathsq, sqv[ipdsq])
until ipdsq = sqnil
end
until pathsq = sqnil;
{ Try pawn interpositions }
if not bbisreset(goodpawnbb) then
if not issamebfile(goodkingsq, checkersq) then
begin
{ Single square advance pawn interpositions }
bband2c2(ipdbb, goodpawnbb, fmbb);
repeat
ipdsq := bbnextsq(ipdbb);
if ipdsq <> sqnil then
begin
pathsq := sqdirtonextsq[ipdsq, advdir];
if bbtestsq(cvpwbb, pathsq) then
if mapsqtobrank(pathsq) <> r8brank then
gmspushm1(gms, ipdsq, pathsq, goodpawn)
else
gmspushpshold(gms, ipdsq, pathsq, goodpawn)
end
until ipdsq = sqnil;
{ Double square advance pawn interpositions }
bband2(pathbb, cvpwbb, brankbbvec[r4brank]);
if not bbisreset(pathbb) then
begin
bband2(ipdbb, goodpawnbb, brankbbvec[r2brank]);
bband2c2d(ipdbb, fmbb);
repeat
ipdsq := bbnextsq(ipdbb);
if ipdsq <> sqnil then
begin
pathsq := sqdirtonextsq[ipdsq, advdir];
if not bbtestsq(merge, pathsq) then
begin
pathsq := sqdirtonextsq[pathsq, advdir];
if bbtestsq(pathbb, pathsq) then
gmspushm1(gms, ipdsq, pathsq, goodpawn)
end
end
until ipdsq = sqnil
end
end
end;
{ Attempt en passant capture }
if epsq <> sqnil then
begin
bband2(defbb, goodpawnbb, pawnatkbbvec[evil, epsq]);
repeat
defsq := bbnextsq(defbb);
if defsq <> sqnil then
begin
movesynthm4(epmove, defsq, epsq, goodpawn, mscepc);
if postestepmove(pos, epmove) then gmspush(gms, epmove)
end
until defsq = sqnil
end;
end
end; { posgenerateevasion }
procedure posgeneratenocheck;
var
goodkingsq: sqtype;
goodbb, evilbb, destbb: bbtype;
frbb, tobb: bbtype;
frsq, tosq: sqxtype;
frman: manrtype;
frbrank: branktype;
r2brank, r7brank: branktype;
r2flag, r7flag: boolean;
advdir, capdir: dirtype;
resdir: dirxtype;
pawnatkbb: bbtype;
epmove: movetype;
castle: castletype;
begin
with pos, board, bbdb do
begin
{ Initialize for the good man scan }
goodkingsq := ksqv[good];
goodbb := locbc[good]; evilbb := locbc[evil];
bbnot1(destbb, goodbb);
bband2c2(frbb, goodbb, fmbb);
r2brank := normalbrank[good, brank2]; r7brank := normalbrank[good, brank7];
{ Loop once for each possible moving man }
repeat
frsq := bbnextsq(frbb);
if frsq <> sqnil then
begin
{ Fetch the moving man and process by its piece kind }
frman := sqv[frsq];
case mantopiece[frman] of
piecep:
begin
{ Set various pre-generation data for this pawn }
frbrank := mapsqtobrank(frsq);
r2flag := frbrank = r2brank; r7flag := frbrank = r7brank;
advdir := pawnadvdir[good];
if bbtestsq(pmbb, frsq) then
resdir := sqsqtodir[goodkingsq, frsq]
else
resdir := dirnil;
pawnatkbb := pawnatkbbvec[good, frsq];
{ Pawn noncapture moves (includes noncapture promotions) }
if (resdir = dirnil) or (resdir = advdir) then
begin
tosq := sqdirtonextsq[frsq, advdir];
if not bbtestsq(merge, tosq) then
if r7flag then gmspushpshold(gms, frsq, tosq, frman)
else
begin
gmspushm1(gms, frsq, tosq, frman);
if r2flag then
begin
tosq := sqdirtonextsq[tosq, advdir];
if not bbtestsq(merge, tosq) then gmspushm1(gms, frsq, tosq, frman);
end
end
end;
{ Pawn capture moves (includes capture promotions; not en passant) }
bband2(tobb, pawnatkbb, evilbb);
repeat
tosq := bbnextsq(tobb);
if tosq <> sqnil then
begin
capdir := sqsqtodir[frsq, tosq];
if (resdir = dirnil) or (resdir = capdir) then
if r7flag then
gmspushpscapt(gms, frsq, tosq, frman, sqv[tosq])
else
gmspushm2(gms, frsq, tosq, frman, sqv[tosq])
end
until tosq = sqnil;
{ En passant capture }
if epsq <> sqnil then
if bbtestsq(pawnatkbb, epsq) then
begin
movesynthm4(epmove, frsq, epsq, frman, mscepc);
if postestepmove(pos, epmove) then gmspush(gms, epmove)
end
end;
piecen:
begin
{ Regular knight moves }
bband2(tobb, atkfs[frsq], destbb);
repeat
tosq := bbnextsq(tobb);
if tosq <> sqnil then gmspushm2(gms, frsq, tosq, frman, sqv[tosq])
until tosq = sqnil
end;
pieceb, piecer, pieceq:
begin
{ Regular sweeper moves }
bband2(tobb, atkfs[frsq], destbb);
if bbtestsq(pmbb, frsq) then bband2d(tobb, beamerbbvec[goodkingsq, frsq]);
repeat
tosq := bbnextsq(tobb);
if tosq <> sqnil then gmspushm2(gms, frsq, tosq, frman, sqv[tosq])
until tosq = sqnil
end;
piecek:
begin
{ Regular king moves }
bband2(tobb, atkfs[frsq], destbb); bband2c2d(tobb, atkbc[evil]);
repeat
tosq := bbnextsq(tobb);
if tosq <> sqnil then gmspushm2(gms, frsq, tosq, frman, sqv[tosq])
until tosq = sqnil;
{ Castling }
if cvas <> 0 then
for castle := castlewq to castlebk do
if postestcastle(pos, castle) then gmspush(gms, cdrvec[castle].cmov)
end;
end
end
until frsq = sqnil;
end
end; { posgeneratenocheck }
begin
with pos do
begin
gmsreset(gms);
if inch then posgenerateevasion else posgeneratenocheck
end
end; { posgenerate }
-
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: The basic move generation
The bulk move counter is also in place and is reasonably fast. It is essentially the same as the move generation routine except that it only has to count and doesn't have to synthesize and stack moves.
The fast mate detector is not yet in place. It will be essentially the same as the bulk move counter, but needs only to return a "count = 0" status. This allows for extensive short circuit calculation.
Other generation routines to come:
1) posgenerategainers (generates captures and promotions only)
2) posgeneratechecks (generates checking moves only)
Both of the above can be called only when the moving color is not in check.
Other related routines, already in place:
1) posmetagenmarked (generate moves and set move flags for SAN disambiguation, checks, and checkmates)
2) posmetagencanonical (generate marked, and then sort by SAN)
3) posmetagensuperdeluxe (generate canonical than add all appropriate draw indication flags; also, set draw and mating scores where needed)
I've taken out all use of the "@" (take address of) operator as I felt it went against the spirit of Pascal and made the code look too much like C.
The fast mate detector is not yet in place. It will be essentially the same as the bulk move counter, but needs only to return a "count = 0" status. This allows for extensive short circuit calculation.
Other generation routines to come:
1) posgenerategainers (generates captures and promotions only)
2) posgeneratechecks (generates checking moves only)
Both of the above can be called only when the moving color is not in check.
Other related routines, already in place:
1) posmetagenmarked (generate moves and set move flags for SAN disambiguation, checks, and checkmates)
2) posmetagencanonical (generate marked, and then sort by SAN)
3) posmetagensuperdeluxe (generate canonical than add all appropriate draw indication flags; also, set draw and mating scores where needed)
I've taken out all use of the "@" (take address of) operator as I felt it went against the spirit of Pascal and made the code look too much like C.