I forgot to mention I am using int _board[256] with int * board = _board+64
and apply 0x88 stuff with 'border' squares
Any way, I am wondering maybe I should withdraw my original assumptions
But even if I were not using the most efficient approaches to things, it still didnt seem I should suffer 100-200% slow-down.
Am I wrong?
If I could have managed to spend 3x longer than others in my prob-hash() routine, eg., that is still relatively insignificant [ie., not near 100-200%]
My InCheck() routine is similar in kind to TSCP's.
Now, InCheck() is in the top 2 routines when profiling [ IIRC 30-40%]
But even this, If I were doing it less than perfectly, couldnt account for 100-200% slow down in over-all NPS
I have tried rewriting these, but it hardly seems to matter.
InCheck() Gen() Eval()
Just looking for some help as to where I might look next. I ran out of ideas.....
I dont even collect attack information!
Hello David,
I'm not sure how serious you take chess programming (or the hobby).
If you have the slightest interest to write a fairly strong program, I believe there are good reason to start on some good basic foundations
as changes with a good starting foundation don't need "rewriting from scratch" as Uri discussed (not necessary) .
I know all along my recent Snailchess is the slowest in the world, but I am
aware of what I am doing, just as an experiment. When I come to
move_order, snailchess almost has full chess information for order(), eval() and gen() all legal moves with full checking status. But it does not work too well .
I then compiled Fruit and on my P4 1.4 Ghz, snailchess's nps is 40k at game start. Fruit's has 130-150k (I think tscp about 100k). So I reverse
direction and I am doing the NPS route now.
1) int _board[256]- good to ask other's if you should try char.
Even Ed uses unsigned cha WB[64], BB[64] for attack tables.
I'm quite sure char board[128/256] suffice somehow.
2) Decide on using piece list (unsorted ok ) early - ie. piece_count[w/b]
number of elements for both colors updated incrementally at
do/undo(). The good thing is when you restore a captured pc on undo(),
it is the move after the list, so it's easy.
You need to keep permanent 16 x 2 piece_type(PC) and the piece-list
has the index of the PCs.
3) What I am doing now don't need any incheck() function. Just do() a
move and when gen() at next ply, gen() in the order bishop, rook,
queen, .... and if ther is "capture" king, previous move is invalid and
gen() returns.
4) You can eliminate some easy(trivial) illegal king's move when a king's
to-sq runs into the attack-bitboards of opponent pawns/knights/king.
Pawns attack bitboard is the cheapest, done in parallel.
N/K can be done by precalculating the bitboard at sq D4 and then
shift by ranks, file to the appropriate square and mask by bits of the
file of sq + 1 or 2 side files. These simple bitboards also allow easy
determination of checkmoves of pawns/knight/king and so no need
to call incheck() here. When a B/R/Q captures a king and returns,
collect the
struct{
int attacker_capture_king_sq;
int brq_step.
}
and with these info you may list thru the remaining move and
can know which balance of moves in your list are xray-invalid.
Fo the list 1 ply down, you can know similar "xray-checks" and set the
move bits, again no need incheck() again(in most cases).
There may be more details which you can know yourself.
I have my PC and board strutures:
- Code: Select all
typedef struct{
char on;
char c;
char pc;
char sq88;
unsigned long info;
}PC;
PC piece[2][16];
char board[128];
char pcInd[2][16];
Hope these helps.
Rasjid