Did anyone write a xiangqi chess engine?

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Dann Corbit
Posts: 12537
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: Did anyone write a xiangqi chess engine?

Post by Dann Corbit »

Shogi is next. You get to drop pieces out of your hand onto the board during the game, not just for promotion.
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
User avatar
hgm
Posts: 27787
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Did anyone write a xiangqi chess engine?

Post by hgm »

maksimKorzh wrote: Sat Jan 23, 2021 1:05 am Oh my god! Oh my god!!!
I've just tried to play this game!!!
Ahhhh!!!
What a fantastic experience!
What an exciting stuff!

Now I will definitely go for coding an engine for it, I swear!
Also I've just realized that I want to master this game as a human player.
Yes, it is a rather exciting game. At high level it suffers from the same drawishness problem as FIDE Chess, though. As Dann says, Shogi is also very exciting, and doesn't have a drawishness problem.
I know chess variants are off topic here but I hope it would be fine answer some questions within this thread)
Guys I don't know why but I feel so happy!
What gave you that idea? :shock: Chess variants are very much on topic here. In fact they are the topic: FIDE Chess is just one of the variants, with rules quite different from the original game as it existed 1300 years ago. And it is not even the most popular variant in the world (which is Xiangqi).

Ask anything you like. I never played it myself, but it is amazing how much you learn from watching engines play. And I did that a lot.
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Did anyone write a xiangqi chess engine?

Post by maksimKorzh »

hgm wrote: Sat Jan 23, 2021 10:19 am
maksimKorzh wrote: Sat Jan 23, 2021 1:05 am Oh my god! Oh my god!!!
I've just tried to play this game!!!
Ahhhh!!!
What a fantastic experience!
What an exciting stuff!

Now I will definitely go for coding an engine for it, I swear!
Also I've just realized that I want to master this game as a human player.
Yes, it is a rather exciting game. At high level it suffers from the same drawishness problem as FIDE Chess, though. As Dann says, Shogi is also very exciting, and doesn't have a drawishness problem.
I know chess variants are off topic here but I hope it would be fine answer some questions within this thread)
Guys I don't know why but I feel so happy!
What gave you that idea? :shock: Chess variants are very much on topic here. In fact they are the topic: FIDE Chess is just one of the variants, with rules quite different from the original game as it existed 1300 years ago. And it is not even the most popular variant in the world (which is Xiangqi).

Ask anything you like. I never played it myself, but it is amazing how much you learn from watching engines play. And I did that a lot.
Thanks for your support mr.Muller.
Today I'm starting my very first xiangqi engine so I'would probably start torturing you soon with uprising questions)))
P.S. Btw today is also a release of WukongJS 1.5
https://github.com/maksimKorzh/wukongJS
Ferdy
Posts: 4833
Joined: Sun Aug 10, 2008 3:15 pm
Location: Philippines

Re: Did anyone write a xiangqi chess engine?

Post by Ferdy »

maksimKorzh wrote: Fri Jan 22, 2021 11:13 pm Any perft tests available for xiangqi?
Maybe using existing engines to calibrate movegen is on the cards?
Any positions like Kiwipete to catch the most common bugs of movegen?
Is FEN-like position representation available?
PGN?
Move format?
I got here a perft data based from Makulit xiangqi engine.

You may post here if your figures does not agree with it.
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Did anyone write a xiangqi chess engine?

Post by maksimKorzh »

Oh, thank you so much, Ferdinand!
That's exactly what I was looking for!
Just brilliant!
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Did anyone write a xiangqi chess engine?

Post by maksimKorzh »

Question on winboard
So I'm running it via wine on linux, seems to be working nicely so far.
So it supports UCCI protocol right?
If I want to run JS engine I need to specify to node.exe and path to JS file as command line argument, right?

Question on UCCI move format (seems like UCI):
so let's say we have a move c3c4, let's just assume it exists and it's legal but
what's the layout for file and ranks?

Xiangqi players using very specific notation for game which is different from UCI,
so is there a standard to specify files/ranks layout?
FIles horizontal?
Ranks vertical?

So say I'm playing red (white) and want to make move "left canon supports central pawn" -
how it would be encoded in UCI format?

Winboard seems to use somewhat that looks like SAN notation.
Is there a way to see debug like in arena gui where I can track of commands, e.g.
position startpos moves .... ?


EDIT:
Ok, it seems the layout is clear

Files (see board from red perspective from left to right): a b c d e f g h f
Ranks (starting from red perspective from bottom to top): 0 1 2 3 4 5 6 7 8 9

What confuses me is that ranks are starting from 0, not from 1, is that a UCI standard? same for UCCI?
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Did anyone write a xiangqi chess engine?

Post by maksimKorzh »

Ok all above seems to be now clear.
The next confusing thing is mailbox size.
I had a look at some engines - many use 256 array (16x16)
but it seems like 14x11 should be fairly enough (analog of 10x12 for common chess)
With this size pieces already should not jump over ranks.
So why 16x16 used?

To HGM: in maxQi I see b[513] // 16 x 8 + dummy + PST
this is a bit confusing...
So what would be the array size without dummy square and PST? (how many files x ranks)?
User avatar
phhnguyen
Posts: 1434
Joined: Wed Apr 21, 2010 4:58 am
Location: Australia
Full name: Nguyen Hong Pham

Re: Did anyone write a xiangqi chess engine?

Post by phhnguyen »

maksimKorzh wrote: Sun Jan 24, 2021 4:16 am Ok all above seems to be now clear.
The next confusing thing is mailbox size.
I had a look at some engines - many use 256 array (16x16)
but it seems like 14x11 should be fairly enough (analog of 10x12 for common chess)
With this size pieces already should not jump over ranks.
So why 16x16 used?

To HGM: in maxQi I see b[513] // 16 x 8 + dummy + PST
this is a bit confusing...
So what would be the array size without dummy square and PST? (how many files x ranks)?
Theory, you can use the "standard" margins as chess, 2 cells. It means the board 9x10 should become 11x14 (left and right sides can share their columns thus they need 11 instead of 13) as the minimum. Thus you got that size already. If you want to save some commands of dividing, use 16, thus we may have that size 16x14 or 16x16

I have used the mailbox with margins for a while and then realized it could help to save few commands IF only, does important nowadays, especially Xiangqi needs more specific code depending on piece’s type. The large board is not free but including some other disadvantages. After that, whenever I need a mailbox for Xiangqi, I simply use 9x10 = 90 cells and quite happy with its simpleness.

Here if you want to see the real code:

Code: Select all

Piece pieces[90];
Source: https://github.com/nguyenpham/FelicityE ... gtbBoard.h

The move generator is quite straightforward and simple too:

Code: Select all

void EgtbBoard::gen(MoveList& moves, Side side, bool captureOnly) const {
    moves.reset();
    for (int l = 0; l < 16; l++) {
        auto pos = pieceList[static_cast<int>(side)][l];
        if (pos < 0) {
            continue;
        }
        auto piece = pieces[pos];

        switch (piece.type) {
            case PieceType::king:
            {
                int col = pos % 9;
                if (col != 3) { // go left
                    gen_addMove(moves, pos, pos - 1, captureOnly);
                }
                if (col != 5) { // right
                    gen_addMove(moves, pos, pos + 1, captureOnly);
                }
                if (pos > 72 || (pos > 8 && pos < 27)) { // up
                    gen_addMove(moves, pos, pos - 9, captureOnly);
                }
                if (pos < 18 || (pos > 63 && pos < 81)) { // down
                    gen_addMove(moves, pos, pos + 9, captureOnly);
                }
                break;
            }

            case PieceType::advisor:
            {
                int y = pos - 10;   /* go left up */
                if (y == 3 || y == 13 || y == 66 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 8;        /* go right up */
                if (y == 5 || y == 13 || y == 68 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 8;        /* go left down */
                if (y == 13 || y == 21 || y == 84 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 10;       /* go right down */
                if (y == 13 || y == 23 || y == 76 || y == 86) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::elephant:
            {
                int y = pos - 20; /* go left up */
                if ((y == 2 || y == 6 || y == 18 || y == 22 || y == 47 || y == 51 || y == 63 || y == 67) && isEmpty(pos - 10)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 16; /* go right up */
                if ((y == 2 || y == 6 || y == 22 || y == 26 || y == 47 || y == 51 || y == 67 || y == 71) && isEmpty(pos - 8)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 16; /* go left down */
                if ((y == 18 || y == 22 || y == 38 || y == 42 || y == 63 || y == 67 || y == 83 || y == 87) && isEmpty(pos + 8)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 20; /* go right up */
                if ((y == 22 || y == 26 || y == 38 || y == 42 || y == 67 || y == 71 || y == 83 || y == 87) && isEmpty(pos + 10)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::cannon: {
                int col = pos % 9;
                /*
                 * go left
                 */
                int f = 0;

                for (int y=pos - 1; y >= pos - col; y--) {
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f++;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }
                /*
                 * go right
                 */
                f = 0;
                for (int y=pos + 1; y < pos - col + 9; y++) {
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f++;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                f = 0;
                for (int y=pos - 9; y >= 0; y -= 9) { /* go up */
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f += 1 ;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                f = 0;
                for (int y=pos + 9; y < 90; y += 9) { /* go down */
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f += 1 ;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                break;
            }

            case PieceType::rook:
            {
                int col = pos % 9;
                for (int y=pos - 1; y >= pos - col; y--) { /* go left */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }
                }

                for (int y=pos + 1; y < pos - col + 9; y++) { /* go right */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }
                }

                for (int y=pos - 9; y >= 0; y -= 9) { /* go up */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }

                }

                for (int y=pos + 9; y < 90; y += 9) { /* go down */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }

                }
                break;
            }

            case PieceType::horse:
            {
                int col = pos % 9;
                int y = pos - 11;
                int z = pos - 1;
                if (y >= 0 && col > 1 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 19;
                z = pos - 9;
                if (y >= 0 && col > 0 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 17;
                z = pos - 9;
                if (y >= 0 && col < 8 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 7;
                z = pos + 1;
                if (y >= 0 && col < 7 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }

                y = pos + 7;
                z = pos - 1;
                if (y < 90 && col > 1 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 17;
                z = pos + 9;
                if (y < 90 && col > 0 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 19;
                z = pos + 9;
                if (y < 90 && col < 8 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 11;
                z = pos + 1;
                if (y < 90 && col < 7 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::pawn:
            {
                if ((side == Side::black && pos > 44) || (side == Side::white && pos < 45)) {
                    int col = pos % 9;
                    /* go left */
                    if (col > 0) {
                        gen_addMove(moves, pos, pos - 1, captureOnly);
                    }
                    /* go right */
                    if (col < 8) {
                        gen_addMove(moves, pos, pos + 1, captureOnly);
                    }
                }

                if (side == Side::black) {
                    /* go down */
                    if (pos < 81) {
                        gen_addMove(moves, pos, pos + 9, captureOnly);
                    }
                } else {
                    /* go up */
                    if (pos > 8) {
                        gen_addMove(moves, pos, pos - 9, captureOnly);
                    }
                }
                break;
            }

            default:
                break;
        }
    }
}

https://banksiagui.com
The most features chess GUI, based on opensource Banksia - the chess tournament manager
User avatar
maksimKorzh
Posts: 771
Joined: Sat Sep 08, 2018 5:37 pm
Location: Ukraine
Full name: Maksim Korzh

Re: Did anyone write a xiangqi chess engine?

Post by maksimKorzh »

phhnguyen wrote: Sun Jan 24, 2021 6:37 am
maksimKorzh wrote: Sun Jan 24, 2021 4:16 am Ok all above seems to be now clear.
The next confusing thing is mailbox size.
I had a look at some engines - many use 256 array (16x16)
but it seems like 14x11 should be fairly enough (analog of 10x12 for common chess)
With this size pieces already should not jump over ranks.
So why 16x16 used?

To HGM: in maxQi I see b[513] // 16 x 8 + dummy + PST
this is a bit confusing...
So what would be the array size without dummy square and PST? (how many files x ranks)?
Theory, you can use the "standard" margins as chess, 2 cells. It means the board 9x10 should become 11x14 (left and right sides can share their columns thus they need 11 instead of 13) as the minimum. Thus you got that size already. If you want to save some commands of dividing, use 16, thus we may have that size 16x14 or 16x16

I have used the mailbox with margins for a while and then realized it could help to save few commands IF only, does important nowadays, especially Xiangqi needs more specific code depending on piece’s type. The large board is not free but including some other disadvantages. After that, whenever I need a mailbox for Xiangqi, I simply use 9x10 = 90 cells and quite happy with its simpleness.

Here if you want to see the real code:

Code: Select all

Piece pieces[90];
Source: https://github.com/nguyenpham/FelicityE ... gtbBoard.h

The move generator is quite straightforward and simple too:

Code: Select all

void EgtbBoard::gen(MoveList& moves, Side side, bool captureOnly) const {
    moves.reset();
    for (int l = 0; l < 16; l++) {
        auto pos = pieceList[static_cast<int>(side)][l];
        if (pos < 0) {
            continue;
        }
        auto piece = pieces[pos];

        switch (piece.type) {
            case PieceType::king:
            {
                int col = pos % 9;
                if (col != 3) { // go left
                    gen_addMove(moves, pos, pos - 1, captureOnly);
                }
                if (col != 5) { // right
                    gen_addMove(moves, pos, pos + 1, captureOnly);
                }
                if (pos > 72 || (pos > 8 && pos < 27)) { // up
                    gen_addMove(moves, pos, pos - 9, captureOnly);
                }
                if (pos < 18 || (pos > 63 && pos < 81)) { // down
                    gen_addMove(moves, pos, pos + 9, captureOnly);
                }
                break;
            }

            case PieceType::advisor:
            {
                int y = pos - 10;   /* go left up */
                if (y == 3 || y == 13 || y == 66 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 8;        /* go right up */
                if (y == 5 || y == 13 || y == 68 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 8;        /* go left down */
                if (y == 13 || y == 21 || y == 84 || y == 76) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 10;       /* go right down */
                if (y == 13 || y == 23 || y == 76 || y == 86) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::elephant:
            {
                int y = pos - 20; /* go left up */
                if ((y == 2 || y == 6 || y == 18 || y == 22 || y == 47 || y == 51 || y == 63 || y == 67) && isEmpty(pos - 10)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 16; /* go right up */
                if ((y == 2 || y == 6 || y == 22 || y == 26 || y == 47 || y == 51 || y == 67 || y == 71) && isEmpty(pos - 8)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 16; /* go left down */
                if ((y == 18 || y == 22 || y == 38 || y == 42 || y == 63 || y == 67 || y == 83 || y == 87) && isEmpty(pos + 8)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 20; /* go right up */
                if ((y == 22 || y == 26 || y == 38 || y == 42 || y == 67 || y == 71 || y == 83 || y == 87) && isEmpty(pos + 10)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::cannon: {
                int col = pos % 9;
                /*
                 * go left
                 */
                int f = 0;

                for (int y=pos - 1; y >= pos - col; y--) {
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f++;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }
                /*
                 * go right
                 */
                f = 0;
                for (int y=pos + 1; y < pos - col + 9; y++) {
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f++;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                f = 0;
                for (int y=pos - 9; y >= 0; y -= 9) { /* go up */
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f += 1 ;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                f = 0;
                for (int y=pos + 9; y < 90; y += 9) { /* go down */
                    if (isEmpty(y)) {
                        if (f == 0 && !captureOnly) {
                            gen_addMove(moves, pos, y, captureOnly);
                        }
                        continue;
                    }
                    f += 1 ;
                    if (f == 2) {
                        gen_addMove(moves, pos, y, captureOnly);
                        break;
                    }
                }

                break;
            }

            case PieceType::rook:
            {
                int col = pos % 9;
                for (int y=pos - 1; y >= pos - col; y--) { /* go left */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }
                }

                for (int y=pos + 1; y < pos - col + 9; y++) { /* go right */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }
                }

                for (int y=pos - 9; y >= 0; y -= 9) { /* go up */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }

                }

                for (int y=pos + 9; y < 90; y += 9) { /* go down */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }

                }
                break;
            }

            case PieceType::horse:
            {
                int col = pos % 9;
                int y = pos - 11;
                int z = pos - 1;
                if (y >= 0 && col > 1 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 19;
                z = pos - 9;
                if (y >= 0 && col > 0 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 17;
                z = pos - 9;
                if (y >= 0 && col < 8 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos - 7;
                z = pos + 1;
                if (y >= 0 && col < 7 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }

                y = pos + 7;
                z = pos - 1;
                if (y < 90 && col > 1 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 17;
                z = pos + 9;
                if (y < 90 && col > 0 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 19;
                z = pos + 9;
                if (y < 90 && col < 8 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                y = pos + 11;
                z = pos + 1;
                if (y < 90 && col < 7 && isEmpty(z)) {
                    gen_addMove(moves, pos, y, captureOnly);
                }
                break;
            }

            case PieceType::pawn:
            {
                if ((side == Side::black && pos > 44) || (side == Side::white && pos < 45)) {
                    int col = pos % 9;
                    /* go left */
                    if (col > 0) {
                        gen_addMove(moves, pos, pos - 1, captureOnly);
                    }
                    /* go right */
                    if (col < 8) {
                        gen_addMove(moves, pos, pos + 1, captureOnly);
                    }
                }

                if (side == Side::black) {
                    /* go down */
                    if (pos < 81) {
                        gen_addMove(moves, pos, pos + 9, captureOnly);
                    }
                } else {
                    /* go up */
                    if (pos > 8) {
                        gen_addMove(moves, pos, pos - 9, captureOnly);
                    }
                }
                break;
            }

            default:
                break;
        }
    }
}

Hold on a sec, so this code snippet relies on 9x10 array? I just can't figure out how/where do you check for offboard squares?
User avatar
phhnguyen
Posts: 1434
Joined: Wed Apr 21, 2010 4:58 am
Location: Australia
Full name: Nguyen Hong Pham

Re: Did anyone write a xiangqi chess engine?

Post by phhnguyen »

maksimKorzh wrote: Sun Jan 24, 2021 9:38 am Hold on a sec, so this code snippet relies on 9x10 array?
Yes, so simple and straightforward, isn't it? :)
maksimKorzh wrote: Sun Jan 24, 2021 9:38 am I just can't figure out how/where do you check for offboard squares?

You can do everything with IF-ELSE. Using mailbox margins is still required IF-ELSE to check offboards. For chess, that is a clever way to simplify the code generator and to reduce the number of IF-ELSE a bit. However, from my experience, it didn't save much for Xiangqi but bringing some disadvantages.

If you look closer to my code to generate moves for Rooks, for example:

Code: Select all

                int col = pos % 9;
                for (int y=pos - 1; y >= pos - col; y--) { /* go left */
                    gen_addMove(moves, pos, y, captureOnly);
                    if (!isEmpty(y)) {
                        break;
                    }
                }


You can see the code for checking offboard squares is embedded inside the FOR-loop, i.e., "y >= pos - col". If that condition is false, you are outside of the board.
https://banksiagui.com
The most features chess GUI, based on opensource Banksia - the chess tournament manager