Author: Tony Werten
Date: 22:51:36 10/29/02
Go up one level in this thread
On October 29, 2002 at 19:55:08, Vincent Diepeveen wrote:
>On October 29, 2002 at 18:51:53, Gerd Isenberg wrote:
>
>><snip>
>>>>>You could go for something cheap to detect checks in qsearch
>>>>>and just see whether the 'to' square possibly attacks the king
>>>>>square, only after that call a function.
>>>>>
>>>>>if( quickchecktable[piece][63+kingsq-to]
>>>>> && slowcheck(piece,to,kingsq) )
>>>>> check = true; ...
>>>>
>>>>In that case I may miss checks that are not done by the piece that moved.
>>>
>>>You are concerned about xray checks?
>>>
>>>In DIEP i do things like that, but i don't know many engines
>>>which do checks in qsearch which do them. Note that i do
>>>unlimited checks in the qsearch for like 32
>>>ply in a row or so (my stackdepth of qsearch currently is 32 ply
>>>i did see no reason yet to make that more).
>>>
>>>In any case, here is how i do things in qsearch, but i have to
>>>warn you that it's very slow if you want to get a million
>>>nodes a second :)
>>>
>>>Note that you see i'm using incremental attack tables too.
>>>
>>>For a number of years i could do without incremental attack tables
>>>by simply only using them in evaluation a lot and quite a bit less
>>>in move ordering.
>>>
>>>>I still have ways to improve the speed without missing checks.
>>>
>>>In case you want to do *all* checks, then you might want to use
>>>attacktables too.
>>>
>>>Note that i simply generate all moves in qsearch. Total move generation
>>>all together is like 0.6% system time anyway.
>>>
>>>Then i can do all moves from which i gamble that they are making my
>>>search more quiet. With big evaluation not necessarily captures and
>>>checks are the only moves that can modify evaluation a lot.
>>>
>>>>Note that I cannot use the difference in the squares to decide if a rook pseudo
>>>>attack the king because the difference between h2 and a3 is the same as the
>>>>difference between a3 and b3.
>>>
>>>but it's very fast to use such a small table that eliminates already the
>>>vaste majority.
>>>
>>>But indeed i also got rid of that primitive table and use a bigger table
>>>now. The primitive table works great however for programs that want to
>>>get a million nodes a second and extend check evasions (so we do not
>>>talk about qsearch here but about normal search where of course
>>>calling a function to detect whether you are in check is way way too
>>>slow).
>>>
>>>Here is some code from DIEP hoping you will have a look at it:
>>>
>>> /* Eerst controleren op schaak */
>>> if( piece != king && looprichting[piece][t][OpKing]
>>> && ScanAttack(side,piece,t,OpKing) ) {
>>> nt->zet |= move_checks;
>>> }
>>>
>>> /* Aftrekschaak */
>>> if( (AttM[f]&(ctlB|ctlR|ctlQ)) ) {
>>> if( ((AttM[f]&ctlB) && looprichting[bishop][f][OpKing]
>>> && ScanXrayAttack(side,bishop,f,OpKing))
>>> || ((AttM[f]&ctlR) && looprichting[rook][f][OpKing]
>>> && ScanXrayAttack(side,rook,f,OpKing))
>>> || ((AttM[f]&ctlQ) && looprichting[queen][f][OpKing]
>>> && ScanXrayAttack(side,queen,f,OpKing)) ) {
>>> int bb,bc,bd,be;
>>> bb = board[t];
>>> bc = color[t];
>>> be = snelbord[f];
>>> bd = snelbord[t];
>>> snelbord[f] = 0;
>>> snelbord[t] = 7;
>>> board[f] = 0;
>>> color[f] = neutral;
>>> board[t] = 7; /* zodat schaakje niet van het stuk zelf is */
>>> color[t] = xside;
>>> if( SqAttackedBySide(OpKing,side) ) {
>>> nt->zet |= move_checks;
>>> aftrekschaak = 1;
>>> /* #if ForwardPruning
>>> nt->zet |= move_noprune;
>>> #endif*/
>>> }
>>> board[f] = piece;
>>> color[f] = side;
>>> board[t] = bb;
>>> color[t] = bc;
>>> snelbord[f] = be;
>>> snelbord[t] = bd;
>>> }
>>> }
>>>
>>>
>>>
>><snip>
>>
>>Hi Vincent,
>>nice to see some code from DIEP, interesting.
>>
>>I guess the declaration for looprichting is something like this:
>>
>>bool looprichting[12][64][64]; // 48KB or 40KB for [10]?
>
>int looprichting[7][64][64];
>
>of course only elements 1..5 get used practical for this check
>generation so it is:
> 5 * 4096 * 4 = 80KB
It might be worth it to convert the 2 squares to 0x88 representation here and
only need 5 * 239 * 4 = 4.78 KB ?
Tony
>
>I do not believe in mixing char/bool/int in a program is an easy
>way to make a program. It needs tuning for each new generation of
>processors. Of course it could speedup code 5-10% depending upon
>the processor.
>
>But for writing good readable compatible code with not a single
>partial register stall, obviously making everything 32 bits is
>the best way to go.
>
>I can imagine that because of the major speed loss, mixing 64
>bits code with 32 bits code in Isichess is not so dumb.
>
>Mixing 8 bits code + 32 bits code is something which is no good show
>to students. It asks for suffering and trouble. the more code there is
>the more likeliness for bugs there even.
>
>>I do it with bitboards in a similar way, but use an array of function pointers
>>for each piece. For sliding pieces i use also currently a two dimensional array
>>(bitboards which makes 32KB) indexed by "to" and "king" to get a bitboard
>>looking for some pieces inbetween.
>
>This array which i use above when using 8 bits indexing is 10KB.
>
>>Due to mememory latency, i am thinking about a short direction fill, to
>>determine these "inter"-bitboards. May be an array of function pointers indexed
>>by let say (8+to-king) with dedicated functions that do directions fills with
>>1..7 iterations without any conditional jumps.
>>
>>For "Aftrekschaak" i use bitboards for pinned pieces and covered
>>(remove?)checkers, I initialized simultaniously for both sides before
>>move-generation.
>
>I'm not a big fan of writing code for 2 sides. It's waste of time IMHO
>to do that. Only when i see no other option in my evaluation i do it.
>I do it nowhere in search though. I'm 100% sure that despite that
>extra 'side' i reference, my code still is fast there. Fiddling with
>bitboards and attacks there is hell slow i found out when i did some
>tries.
>
>The 64 bits code at todays 32 bits processors is always suffering
>from abnormal penalties. I was for example very
>amazed that the huge stupid code to get a bit out of the bitboards
>at the K7 was faster than doing the 2 vector instructions in a row.
>
>You clearly showed that here and i find it kind of *disgusting*
>that such things get penalties without the average programmer
>even *smelling* that there could be danger there.
>
>Optimizing for a certain processor like Dieter and Frans and many
>others (Ed!) always use(d) to do is really a fulltime job IMHO.
>
>That 2 branches at the K7 which suffer for sure 1 misprediction,
>that this still is faster than 2 vector instructions in a row
>really convinces me that the x86 processors still have a long
>way to go.
>
>I am therefore really happy with the R14000/McKinley processors.
>
>No weird behaviour on these things! Writing code as i do works
>great for those processors! The R14000 has a L2 cache of 8MB and
>the McKinley from 3 MB (L3), so obviously using such lookup arrays
>is no problem there.
>
>It must be disaster to use 64 bits at pc processors today, because
>you need to test and retest everything you do with it. Also assumption
>of crafty inline assembly that compilers put everything is in certain
>registers IMHO is in itself a sick assumption. Doesn't that give the
>compilers less possibilities to optimize the code in your opinion?
>
>Such functions as get called here like in the above DIEP code, you
>can do very fast in integer code with a minimum of mispredictions.
>
>However as you can see it is not optimized for speed that much. As i already
>indicate, if my program would be very small and like Quest a look
>like assembly engines had to run within L1 cache, the array sizes (80KB)
>are already too big to consider to be useful. DIEP is using that
>L2 data cache very intensively. Hopefully it gets efficiently
>prefetched also at the K7 MP processor.
>
>In the board of course the gnuchess pawn codes
>are there (pawn = 1, king = 6), so for pawns you need in the function
>some quick code to determine check or not. that's a few
>integer instructions as we all know. No need to fiddle with slow
>64 bits there.
>
>>I have to look whether "from" is member of "remove checkers" bitboard and
>>whether the move leaves the ray.
>
>Why do you use a lookup table for functions Gerd,
>ain't there faster alternatives without defining a bunch of
>functions?
>
>In my code i already lose of course in advance the difference between
>white and black. Then secondly i don't need to check for the queen
>seperately as that's bishop + rook. So all i need is like 1 'if'
>statement extra which saves out a lookup function table.
>
>>Regards,
>>Gerd
>>
>>
>>// two polymorph routines
>>Bool CNode::IsCheckMove(UINT to, UINT piece) const {
>> return (this->*m_scIsCheckMove[piece])(to);}
>>
>>Bool CNode::IsCheckMove(UINT from, UINT to, UINT piece) const {
>> return IsCheckMove(to, piece) || IsRemoveCheckMove(from, to);}
>>
>>....
>>
>>typedef Bool (CNode::*PTR_ISCHECKMOVE)(UINT to) const;
>>
>>CNode::PTR_ISCHECKMOVE CNode::m_scIsCheckMove[14] =
>>{
>> KingCheckMove,
>> KingCheckMove,
>> WPawnCheckMove,
>> BPawnCheckMove,
>> BishopCheckMove,
>> BishopCheckMove,
>> KnightCheckMove,
>> KnightCheckMove,
>> RookCheckMove,
>> RookCheckMove,
>> KingCheckMove,
>> KingCheckMove,
>> QueenCheckMove,
>> QueenCheckMove,
>>};
>>
>>Bool CNode::KingCheckMove(UINT to) const
>>{
>> to;
>> ASSERT(false);
>> return false;
>>}
>>
>>
>>Bool CNode::WPawnCheckMove(UINT to) const
>>{
>> UINT eksq = EnemyKingSquare();
>> if ( isDiagonalAdjacent(eksq, to) )
>> {
>> register int delta = eksq - to;
>> return (delta == 7) + (delta == 9);
>> }
>> if (to >= 56 )
>> return QueenCheckMove(to);
>> return false;
>>}
>>
>>...
>>
>>Bool CNode::BishopCheckMove(UINT to) const
>>{
>> UINT eksq = EnemyKingSquare();
>> if ( isDiagonalAdjacent(eksq, to) )
>> return true;
>> if ( isDiagonal(eksq, to) )
>> return (m_sInterBB[eksq][to] & GetPieceBB()) == 0;
>> return false;
>>}
>>
>>...
This page took 0 seconds to execute
Last modified: Thu, 15 Apr 21 08:11:13 -0700
Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.