Author: Russell Reagan
Date: 09:46:42 08/01/03
Go up one level in this thread
On August 01, 2003 at 06:14:27, Bo Persson wrote:
>Of course it can be empty! The default constructor Piece() results in an empty
>position.
I guess I would call this something else then. Like maybe SquareContents instead
of Piece. It seems more correct that SquareContents could be empty than a Piece.
>My Square class doesn't hold pieces, but is used to index the board. The board
>contains an array of pieces (and some bitmaps as an alternate representaion).
This is basically what I do too, but I don't think you can hide the
implementation very well without losing a little bit of speed. For instance, if
you truly hide the implementation, then you should be able to take your Board
that uses bitboards and turn it into an 0x88 based Board without having to
change Square or Piece or anything else. If you use a 64-element array in your
bitboard based Board, then it won't work correctly when you move to a
128-element array for 0x88 (assuming Square is just a wrapper for a board
index).
That is my point. Writing wrappers for these things is fine, but to really hide
the implementation as an OOP purist would have you do would mean your Board
class would either be sub-optimal or Square would have to know how Board worked.
>It doesn't have to be slower, and I actually get less of a headache when the
>code is "pretty".
If you want to really hide the implementation, I think it causes a lot more
headache, because what you are currently doing doesn't hide the implementation
as far as I can see, because Board and Square know how each other work.
I think that writing classes for things like Square is good, because then you
can do things like verify (assert or however you'd like) that the values are
always legitimate for that class. You can also make sure that the private stuff
only gets modified how you want it to be modified. But I think that's where the
advantages stop. If you carry on with the OOP philosophy and try to start hiding
the implementation, you will have to make your program slower, or do more work
to implement some kind of generic classes that will work with bitboards or array
based approaches.
Part of the reason that bitboards work is because many parts of the program know
that bitboards are being used. Same goes for 0x88 or 16x16 or whatever approach
you use. If you hide the knowledge that the board is using bitboards inside the
board class, many of the advantages go away. Take evaluation for instance. I
don't think "evaluate" is something a board does, and you're going to have a
hard time evaluating efficiently if you don't know what kind of board
representation you're using.
Part of the OOP approach is that you model the real world problem you're trying
to solve. In the real world, you don't have empty pieces, or boards that
evaluate themselves. If you're going to forget about all of that and start
creating fictitious things like empty pieces, then I think my point is shown,
that OOP is only applicable to computer chess to a limited degree.
>Extra work for me or for the computer?
For the programmer. Like I've said, I don't think you're really hiding the
implementation. If you chose to do so, you would create a lot more work and
headache for yourself, or if you were able to avoid that it would probably be
slower than it currently is.
>I haven't though of this explicitly of this before, but it seems like I treat
>bitbards as piece set objects. Glad you asked!
And that works for bitboards, but if you moved to 0x88, I think your program
would slow down significantly if you tried performing "piece set intersections"
all over the place like you do when you use bitboards, because that isn't the
"0x88 way" of doing things.
>The move generator is created with a reference (or is it a pointer?) to the
>current position, a board class. It can then ask for UnmovedPieces to get
>candidates for pieces to move (or rather the set of squares where these pieces
>reside).
>
>Then loop, something like
>
>   while (!MovingPieces.isEmpty())
>   {
>      const Square FromSquare = MovingPieces.GetOne();
>
>      BitBoard Targets = Board.AttacksFrom(FromSquare);
>      Targets.RestrictTo(AllPieces(Opponent()));   // to get captures only
>
>      while (!Targets.isEmpty())
>      {
>         const Square ToSquare = Targets.GetOne();
>
>         MoveList.Add(FromSquare, ToSquare, etc.);
>      }
>   }
>
>from the top of my head...
So if you truly hide the implementation, this code should still work if all you
do is change your Board to an 0x88 based class. So no changes to Square, Piece,
Bitboard, or anything else. Just Board. I think that's going to be WAY slower
(assuming it will even work), because you're going to be doing loops like crazy
to simulate bitboards as piece sets. It might be possible to rewrite your piece
set (bitboard) class to work well with 0x88, but that defeats the purpose of
hiding the implementation if you have to re-implement half your classes to make
a change to one of them.
Maybe I'm being too picky or legalistic in programming in an OO way. *shrug*
What I do is probably similar to what you do. For instance I have a Color class.
// from memory, untested
enum ColorValue { white, black };
class Color {
    private:
        typedef int ColorType;
        ColorType color;
        bool invariant () {
            static const int some_val = 0xcccccccc;
            assert(color == white || color == black);
            assert(some_val == 0xcccccccc);
        }
    public:
        Color (ColorValue value) : color(value) { assert(invariant(); }
        operator ColorType () { assert(invariant(); return color; }
        void change () { assert(invariant(); color ^= 1; }
        char to_char () { assert(invariant(); return color_chars[color]; }
        const char * to_string () { assert(invariant(); return
color_strings[color]; }
};
I don't really aim for the implementation to be hidden, but rather to ensure
that a Color always contains what it's supposed to. For instance, if I had an
array elsewhere in my program that got overrun, and garbage started getting
written over a Color's private information, the assert stuff should catch it. I
added in the some_val thing because the array overrun could easily write all
0's, and my assert wouldn't catch that because white is 0, and the some_val and
all of the assert stuff should go away in a release compile.
Anyway, that's how I'm doing things. Thanks for sharing :)
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.