>The cause was making a null-move did not clear the
>enpassant capture status, so that white could initiate the following "trick"
>to win a pawn: e4 <null> dxe3. That is, white could capture it's own pawn
>in a bizarre way.
I'm so glad to see that even Bob makes mistakes like this... :-)
My program not only captured it's own pawn this way, but when it "unmade"
the move, it put back a black pawn instead of a white one. Then on the
next un-make move for this side, it put the pawn back on e2. It took
me some time to figure out why my white pawns on the second rank kept
turning into black pawns!
So if anyone out there is just starting to implement null moves in their
program, watch out for this one! You have to save the value of the en-passant
flag before making the null move, clear the flag after making the null move,
then restore the flag after un-making the null move.
Joe S.
Interesting point about Crafty's internal data structure. If you have
studied it, you will notice that there is no "Unmake()" function. To
make a move, I take {position[ply],move} -> position[ply+1]. As a
result, I have no need to unmake a move, just simply ply-- and it's
"done."
This then "hides" such silly errors as I produced above. Since errors
can only propogate in a forward direction, I never end up with a root
position that looks "funny" although the search could have gone south,
turned left, and gotten lost. The speed of this approach seems better
than the old cray blitz approach, which did have a make and unmake
mechanism, but it certainly lacks a lot in the area of debugging. If
I make an illegal move, the resulting search is certainly wrong, but
the error can never propogate back past the point where the error was
made, very often completely "hiding" it.
--
Robert Hyatt Computer and Information Sciences
hy...@cis.uab.edu University of Alabama at Birmingham
(205) 934-2213 115A Campbell Hall, UAB Station
(205) 934-5473 FAX Birmingham, AL 35294-1170
Cheers,
Tom
P.S. Leaving for Paderborn in < 6 hours!!!
------------------------------------------------------------------------------
Interpreter, n.:
One who enables two persons of different languages to
understand each other by repeating to each what it would have been to
the interpreter's advantage for the other to have said.
-- Ambrose Bierce, "The Devil's Dictionary"
>Interesting point about Crafty's internal data structure. If you have
>studied it, you will notice that there is no "Unmake()" function. To
>make a move, I take {position[ply],move} -> position[ply+1]. As a
>result, I have no need to unmake a move, just simply ply-- and it's
>"done."
I debated with myself for a long time about doing it this way vs. the
make() - unmake() way. I finally decided that the technique you
describe would be *slower* than using an unmake(), because your
technique requires copying the entire board (as well as all "state"
variables that go along with the board) in order to get position[ply+1].
My guess (I really haven't studied Crafty's code closely) is that
updating all the bitmaps associated with the position is very time
consuming anyway, so creating another "position" is really no big
addition to this. You then save the trouble of "un-doing" the
changes to all the bitmaps, so in the end, this technique is actually
faster for Crafty than unmake() would be. Is this correct?
Joe S.
To an extent. The main point is, on *real* machines, the cost of the copy
is very low. The Cray is a good example. On machines with very poor memory
bandwidth, like the PC, for example, the cost still seems less, but not as
dramatically lower as you might suspect.
Remember, to unmake, you have to fetch an awful lot of instructions for
execution, which will more than offset the cost of moving the position around
one time. Crafty's current position is fairly small, with several things in
the mill to cut it significantly, further reducing this copy penalty.
updating the bitmaps is now trivial with the rotated bitboard stuff and the
table lookup for attack bitmaps, so that I don't have to fool around with
incrementally updating all the attack stuff after each move. I did it the
make/unmake way before, and subjectively this "feels" faster. However, it
would be easy to analyze unmake code and count instructions (which are memory
fetches) and compare against the cost of the copy to ply+1 I do now. It
looks overwhelmingly shorter to me.
Bob