rudzen / ChessLib

C# chess library containing a complete data structure and move generation.
MIT License
81 stars 23 forks source link

[Question] get piece position on board #21

Closed SloPro closed 5 years ago

SloPro commented 5 years ago

I've been scratching my head for a few hours now and I can't seem to figure out the best way to get the position (rank and row) of a piece object. I'm making a basic chess engine and using this library so far has made things a lot easier. I need to get a Piece object's coordinates during board evaluation to use them for accessing my Piece Tables.

I've been looping through the Chess.Position.BoardLayout array, which contains all pieces, using 2 nested loops and returning rank (outer loop iter) and file (inner loop iter) when current array element == piece I'm looking for, but that's not very fast and is slowing my code down a lot. Is there a built in method to do this (I couldn't find one) or do you maybe have any better ideas on how to do this? Cheers

Current code:


int GetPieceCoords(Chess.Game game, Piece piece)
{
    for (int rank = 0; rank < 8; rank++)
    {
        for (int file = 0; file < 8; file++)
        {
            var pos = rank * 8 + file;
            Piece p = game.Position.BoardLayout[pos];
            if (p == piece)
                return pos;
        }
    }
    return -1;
}

EDIT: just found out that this doesn't work as all identical pieces will pass the == check so I am completely out of ideas
rudzen commented 5 years ago

There exists several way to retrieve the pieces from the board. You can retrieve all the pieces through using the Position.Pieces() methods.

There exists several overloads which can supply you with the pieces, depending on what information you have in the evaluation.

For example if I wanted to extract any pieces which are attacked by my rook and do something with it, this would do the trick

        var g = new Game();
        g.SetFen(someFen);
        var pos = g.Position;
        var myColor = PlayerExtensions.White;
        var theirColor = ~myColor;
        var myRookPiece = EPieceType.Rook.MakePiece(myColor);

        // get all my rooks
        var myRooks = pos.Pieces(myRookPiece);

        // get all opponent pieces
        var opponentPieces = pos.Pieces(theirColor);

        foreach (var rook in myRooks)
        {
            // get my rook file and rank
            var file = rook.File();
            var rank = rook.Rank();

            // get all pieces attacked by my rook
            var attackedPieces = rook.GetAttacks(EPieceType.Rook, pos.Pieces()) & opponentPieces;

            // iterate all squares of attacked pieces
            foreach (var attackedEnemyPiece in attackedPieces)
            {
                var pc = pos.GetPiece(attackedEnemyPiece);
                if (pc.Type() == EPieceType.King)
                {
                    // apparently in check
                }

                var opponentRank = attackedEnemyPiece.Rank();
                var opponentFile = attackedEnemyPiece.File();

                if (file == opponentFile)
                {
                    // rook and enemy piece are on same file
                }

                // do something else with the attacked piece square
                // ..
                // ..
            }
        }

This is just an example of how to use it and could be done more tailored for a specific evaluation.

Hope it helps a bit 😄

SloPro commented 5 years ago

Okay thank you for the reply, I've tried it out! I implemented my own hacky way by bitshifting through every EPieces's bitboard to check if a bit is 1 and then using that to determine the position of the piece, and I've tried your code and mine actually seems to be about 2x faster code if you're curious: https://pastebin.com/0TwW7VMu

I have another question as well if you don't mind answering, your transposition table implementation has 2 properties for it's entry class - "StaticValue" and "Value", what's the purpose of the "StaticValue" compared to the normal one? Your documentation in the code just says <param name="statValue">The static value of the move</param> vs <param name="value">The value of the move</param>

Cheers

rudzen commented 5 years ago

Sure np.

The code was just an example as to how you can get the data and not meant to be fast or particularly useful in a real world chess engine.

Some tips:


The "StaticValue" is the evaluation value from the current position, and the "Value" is the value assigned to a move. It is usually used to determine which move pick from all generated moves. See https://www.chessprogramming.org/Move_Ordering for move information. Perhaps that should be more clear in the description.

rudzen commented 5 years ago

Looked a bit more at your approach, and I see what you mean. But instead of iterating from min-bit to max-bit, it would be possible to narrow it down further by just looking at the set squares. For example:

    private static int EvaluatePieceTable(Position pos)
    {
        var total = 0;

        for (var pt = EPieceType.Pawn; pt < EPieceType.PieceTypeNb; ++pt)
            total += GetPieceTableValue(pos, pt, PlayerExtensions.White) - GetPieceTableValue(pos, pt, PlayerExtensions.Black);

        return total;
    }

    // for a single side piece color only!
    // allows for example :
    // GetPieceTableValue(pos, pieceType, WHITE) - GetPieceTableValue(pos, pieceType, BLACK)
    private static int GetPieceTableValue(Position pos, EPieceType pieceType, Player us)
    {
        var pieces = pos.Pieces(pieceType, us);
        var sum = 0;
        foreach (var k in pieces)
            sum += PieceTable[(int) pieceType][us.Side][k.AsInt()];

        return sum;
    }

This is not exactly what you are doing, but illustrates the simplicity of the BitBoard type IEnumerable interface.

SloPro commented 5 years ago

Yeah I see now, the overloads and inumerables are indeed very nice and powerful - they make the code a lot shorter! However it's sadly still a bit slower so I'll keep my own implementation for now

My implementation took (on average, after 5 tests) 1.848s for 3 million evaluations of the starting board position. The set square implementation above took 2.613s for the starting board position. The gap got closer in the endgame position test I ran: Mine 1.863s, yours 2.061s for 3 million evaluations - but still slightly slower.

Thanks for the explanations and tips though, they've been of great help! You can probably close this "issue" now 🙂

Edit: Out of curiosity I tried just swapping out NumberOfSetBits in my implementation for Position.BoardPieces[piece].Count() and it's almost 2x slower (3.45s vs 1.86s)