Did anyone write a xiangqi chess engine?

Discussion of chess software programming and technical issues.

Moderator: Ras

User avatar
hgm
Posts: 28354
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: Fri Jan 29, 2021 4:45 pm I've finally managed to run my javascript xiangqi engine under xboard using linux version of uci2wb
and it even starts calculating but never makes a move for some reason.
Our latest messages crossed.

Please run XBoard as you did, but with the additional option -debug . Then it will create a file (in the current directory) 'xboard.debug'. Post it contents here, so that I can see what is going on.

Since I don't know exactly what you did, it is very possible that you are running the engine as UCCI. This has a 'go' command, but that doesn't use 'wtime' and 'btime' parameters, which might explain why the engine starts thinking indefinitely.
User avatar
maksimKorzh
Posts: 775
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: Fri Jan 29, 2021 5:02 pm
maksimKorzh wrote: Fri Jan 29, 2021 4:45 pm I've finally managed to run my javascript xiangqi engine under xboard using linux version of uci2wb
and it even starts calculating but never makes a move for some reason.
Our latest messages crossed.

Please run XBoard as you did, but with the additional option -debug . Then it will create a file (in the current directory) 'xboard.debug'. Post it contents here, so that I can see what is going on.

Since I don't know exactly what you did, it is very possible that you are running the engine as UCCI. This has a 'go' command, but that doesn't use 'wtime' and 'btime' parameters, which might explain why the engine starts thinking indefinitely.

Here's the output of debug:

Code: Select all

locale = LC_CTYPE=en_US.UTF-8;LC_NUMERIC=uk_UA.UTF-8;LC_TIME=en_US.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=uk_UA.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=uk_UA.UTF-8;LC_NAME=uk_UA.UTF-8;LC_ADDRESS=uk_UA.UTF-8;LC_TELEPHONE=uk_UA.UTF-8;LC_MEASUREMENT=uk_UA.UTF-8;LC_IDENTIFICATION=uk_UA.UTF-8
recognized 'xiangqi' (-1) as variant xiangqi
recognized 'xiangqi' (-1) as variant xiangqi
recognized 'xiangqi' (-1) as variant xiangqi
shuffleOpenings = 0
shuffleOpenings = 0
Version: xboard 4.9.1 + uci2wb ()
Reset(1, 0) from gameMode 0
recognized 'xiangqi' (-1) as variant xiangqi
GameEnds(0, (null), 2)
shuffleOpenings = 0
StartChildProcess (dir=".") uci2wb wukong
291 >first : xboard
protover 2
295 <first : feature setboard=1 usermove=1 debug=1 ping=1 name=1 reuse=0 exclude=1 pause=1 sigint=0 sigterm=0 done=0
295 >first : accepted setboard
295 >first : accepted usermove
295 >first : accepted debug
295 >first : accepted ping
295 >first : accepted name
295 >first : accepted reuse
295 >first : accepted exclude
295 >first : accepted pause
295 >first : accepted sigint
295 >first : accepted sigterm
295 >first : accepted done
295 <first : feature option="UCI2WB debug output -check 0"
295 >first : accepted option
623 <first : feature myname="WukongJS 1.0 (UCI2WB)"
624 >first : accepted myname
624 <first : feature variants="normal,xiangqi"
624 >first : accepted variants
624 <first : feature smp=1 memory=1 done=1
624 >first : accepted smp
624 >first : accepted memory
624 >first : accepted done
625 >first : memory 68
625 >first : cores 1
625 >first : new
random
625 >first : variant xiangqi
625 >first : level 40 5 0
625 >first : post
625 >first : hard
625 >first : easy
625 >first : ping 1
1210 <first : pong 1
nps: w=-1, b=-1
2676 >first : name maksim
nps: w=-1, b=-1
2677 >first : time 30000
2677 >first : otim 30000
book hit = (NULL)
2677 >first : usermove 2677 >first : h2e2
2707 <first :   1      12      2        137 b0c2 
2753 <first :   2       0      7       1321 b0c2 b9c7 
2850 <first :   3      12     17       7497 b0c2 b9c7 h0g2 
2935 <first :   4       0     25      23744 h2c2 b7c7 c2c6 c7c3 
3037 <first :   5      12     35      45899 h2c2 b7c7 h0g2 h9g7 i0h0 
3690 <first :   6       0    100     221331 h2e2 h9g7 b2c2 b7e7 h0g2 b9a7 
4236 <first :   7      10    155     354300 h2e2 h9g7 b2c2 b7e7 h0g2 h7h3 g3g4 h3c3 c2c6 
4236 <first : move h2e2
4237 <first : 
GameEnds(29, xboard exit, 2)
8258 >first : result * {xboard exit}
8258 >first : quit
I think that it's not UCCI is the case...
where can I get UCCI specs?
At least those used by uci2wb/xboard?
User avatar
maksimKorzh
Posts: 775
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 »

Btw "https://hgm.nubati.net/cgi-bin/gitweb.cgi" and some other of your pages giving privacy error in the browser and after adding site to "trusted" browser simply gives page not found (both chrome and firefox), however if I make a straight forward HTTP GET request (I used scrapy shell) it fetches the html page and I can view it in the browser.

Is there an easy way to access those pages?
User avatar
hgm
Posts: 28354
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 »

Well, it is http, and not https. If I use the link you posted I get a security warning too. As http they work for me, both on FireFox and Chrome (Windows versions).

What you see in the xboard.debug file is CECP, used by WinBoard to communicate with the adapter (UCI2WB). It doesn't show what went on between the adapter and the engine (the UCI, or whatever). To see that, you would have to start the 'engine' as

uci2wb debug wukong

The extra 'debug' argument causes UCI2WB to send all communication it has with the engine to the GUI too (as a comment), so that it would appear in the xboard.debug file.

In the example you show the engine did move, however (h2e2). So there doesn't seem to be a problem, at least not in this case. The way you start UCI2WB (without flag argument -x, -c or -s) should put in in standard UCI mode, which is what you want.

The only description of UCCI I know is in the source code of Elephant Eye. (Which is the reference implementation for this protocol.) I hope you read Chinese... Lacking that the best way to know how it works would be to run a UCCI engine under XBoard + UCI2WB with the 'debug' argument to UCI2WB (and -debug to XBoard), and look in the xboard.debug file to see what they sent to each other.
User avatar
maksimKorzh
Posts: 775
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: Fri Jan 29, 2021 6:49 pm Well, it is http, and not https. If I use the link you posted I get a security warning too. As http they work for me, both on FireFox and Chrome (Windows versions).

What you see in the xboard.debug file is CECP, used by WinBoard to communicate with the adapter (UCI2WB). It doesn't show what went on between the adapter and the engine (the UCI, or whatever). To see that, you would have to start the 'engine' as

uci2wb debug wukong

The extra 'debug' argument causes UCI2WB to send all communication it has with the engine to the GUI too (as a comment), so that it would appear in the xboard.debug file.

In the example you show the engine did move, however (h2e2). So there doesn't seem to be a problem, at least not in this case. The way you start UCI2WB (without flag argument -x, -c or -s) should put in in standard UCI mode, which is what you want.

The only description of UCCI I know is in the source code of Elephant Eye. (Which is the reference implementation for this protocol.) I hope you read Chinese... Lacking that the best way to know how it works would be to run a UCCI engine under XBoard + UCI2WB with the 'debug' argument to UCI2WB (and -debug to XBoard), and look in the xboard.debug file to see what they sent to each other.
Thanks, at least it's reasonable steps to try.
I'll post if find something.
Thank you hgm.
User avatar
maksimKorzh
Posts: 775
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 »

Here's the extended debug:

Code: Select all

locale = LC_CTYPE=en_US.UTF-8;LC_NUMERIC=uk_UA.UTF-8;LC_TIME=en_US.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=uk_UA.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=uk_UA.UTF-8;LC_NAME=uk_UA.UTF-8;LC_ADDRESS=uk_UA.UTF-8;LC_TELEPHONE=uk_UA.UTF-8;LC_MEASUREMENT=uk_UA.UTF-8;LC_IDENTIFICATION=uk_UA.UTF-8
recognized 'xiangqi' (-1) as variant xiangqi
recognized 'xiangqi' (-1) as variant xiangqi
recognized 'xiangqi' (-1) as variant xiangqi
shuffleOpenings = 0
shuffleOpenings = 0
Version: xboard 4.9.1 + uci2wb ()
Reset(1, 0) from gameMode 0
recognized 'xiangqi' (-1) as variant xiangqi
GameEnds(0, (null), 2)
shuffleOpenings = 0
StartChildProcess (dir=".") uci2wb debug wukong
1096 >first : xboard
protover 2
1133 <first : # 'xboard' think=0 pause=0 log=0 sent=0
1136 <first : # 'protover' think=0 pause=0 log=0 sent=0
1136 <first : feature setboard=1 usermove=1 debug=1 ping=1 name=1 reuse=0 exclude=1 pause=1 sigint=0 sigterm=0 done=0
1136 >first : accepted setboard
1136 >first : accepted usermove
1136 >first : accepted debug
1136 >first : accepted ping
1136 >first : accepted name
1136 >first : accepted reuse
1136 >first : accepted exclude
1136 >first : accepted pause
1136 >first : accepted sigint
1136 >first : accepted sigterm
1136 >first : accepted done
1136 <first : feature option="UCI2WB debug output -check 1"
1136 >first : accepted option
1136 <first : # uci
2007 <first : # engine said: 
2007 <first : # engine said:   Wukong Xiangqi - UCI mode - v1.0
2007 <first : # engine said: 
2007 <first : # engine said: 
2012 <first : # engine said: id name WukongJS 1.0
2012 <first : feature myname="WukongJS 1.0 (UCI2WB)"
2012 >first : accepted myname
2031 <first : # engine said: id author Code Monkey King
2031 <first : # engine said: option name Hash type spin default 16 min 4 max 128
2031 <first : # engine said: uciok
2031 <first : feature variants="normal,xiangqi"
2031 >first : accepted variants
2031 <first : feature smp=1 memory=1 done=1
2031 >first : accepted smp
2031 >first : accepted memory
2031 >first : accepted done
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2031 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2032 <first : # 'accepted' think=0 pause=0 log=0 sent=0
2033 >first : memory 68
2033 >first : cores 1
2033 >first : new
random
2033 >first : variant xiangqi
2033 >first : level 40 5 0
2033 >first : post
2033 >first : hard
2033 >first : easy
2033 >first : ping 1
Impossible move , type = 0
2039 <first : # 'memory' think=0 pause=0 log=0 sent=0
2039 <first : # 'cores' think=0 pause=0 log=0 sent=0
4394 <first : # 'new' think=0 pause=0 log=0 sent=0
4397 <first : # setoption name Hash value 68
4397 <first : # isready
4397 <first : # engine said: Set hash table size to 68 Mb
4418 <first : # engine said: Hash table initialized with 3565158 entries
4418 <first : # engine said: readyok
4418 <first : # ucinewgame
4418 <first : # 'random' think=0 pause=0 log=0 sent=0
4418 <first : # 'variant' think=0 pause=0 log=0 sent=0
4418 <first : # 'level' think=0 pause=0 log=0 sent=0
4418 <first : # 'post' think=0 pause=0 log=0 sent=0
4418 <first : # 'hard' think=0 pause=0 log=0 sent=0
4418 <first : # 'easy' think=0 pause=0 log=0 sent=0
4418 <first : # 'ping' think=0 pause=0 log=0 sent=0
4418 <first : pong 1
4429 <first : # engine said: 
4429 <first : # engine said: 
4429 <first : # engine said: 9  r n b a k a b n r 
4429 <first : # engine said: 8  . . . . . . . . . 
4429 <first : # engine said: 7  . c . . . . . c . 
4429 <first : # engine said: 6  p . p . p . p . p 
4429 <first : # engine said: 5  . . . . . . . . . 
4429 <first : # engine said: 4  . . . . . . . . . 
4429 <first : # engine said: 3  P . P . P . P . P 
4429 <first : # engine said: 2  . C . . . . . C . 
4429 <first : # engine said: 1  . . . . . . . . . 
4429 <first : # engine said: 0  R N B A K A B N R 
4429 <first : # engine said: 
4429 <first : # engine said:    a b c d e f g h i
4429 <first : # engine said: 
4429 <first : # engine said:    side:           r
4429 <first : # engine said:    hash key:      -1866463866
4429 <first : # engine said:    king squares:  [e0, e9]
4429 <first : # engine said: 
6082 >first : force
StartChildProcess (dir=".") uci2wb debug wukong
6091 >second: xboard
protover 2
6092 <first : # 'force' think=0 pause=0 log=0 sent=0
6120 <second: # 'xboard' think=0 pause=0 log=0 sent=0
6123 <second: # 'protover' think=0 pause=0 log=0 sent=0
6123 <second: feature setboard=1 usermove=1 debug=1 ping=1 name=1 reuse=0 exclude=1 pause=1 sigint=0 sigterm=0 done=0
6123 >second: accepted setboard
6123 >second: accepted usermove
6123 >second: accepted debug
6123 >second: accepted ping
6123 >second: accepted name
6123 >second: accepted reuse
6123 >second: accepted exclude
6123 >second: accepted pause
6123 >second: accepted sigint
6123 >second: accepted sigterm
6123 >second: accepted done
6123 <second: feature option="UCI2WB debug output -check 1"
6123 >second: accepted option
6123 <second: # uci
6978 <second: # engine said: 
6979 <second: # engine said:   Wukong Xiangqi - UCI mode - v1.0
6979 <second: # engine said: 
6979 <second: # engine said: 
6999 <second: # engine said: id name WukongJS 1.0
6999 <second: feature myname="WukongJS 1.0 (UCI2WB)"
6999 >second: accepted myname
7014 <second: # engine said: id author Code Monkey King
7014 <second: # engine said: option name Hash type spin default 16 min 4 max 128
7015 <second: # engine said: uciok
7015 <second: feature variants="normal,xiangqi"
7015 >second: accepted variants
7015 <second: feature smp=1 memory=1 done=1
7015 >second: accepted smp
7015 >second: accepted memory
7015 >second: accepted done
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7015 <second: # 'accepted' think=0 pause=0 log=0 sent=0
7016 >second: memory 68
7016 >second: cores 1
7016 >second: new
random
7016 >second: variant xiangqi
7016 >second: level 40 5 0
7016 >second: post
7016 >second: hard
7016 >second: easy
7016 >second: ping 1
Impossible move , type = 0
7016 >second: force
7036 <second: # 'memory' think=0 pause=0 log=0 sent=0
7036 <second: # 'cores' think=0 pause=0 log=0 sent=0
9226 <second: # 'new' think=0 pause=0 log=0 sent=0
9229 <second: # setoption name Hash value 68
9229 <second: # isready
9229 <second: # engine said: Set hash table size to 68 Mb
9248 <second: # engine said: Hash table initialized with 3565158 entries
9248 <second: # engine said: readyok
9248 <second: # ucinewgame
9248 <second: # 'random' think=0 pause=0 log=0 sent=0
9248 <second: # 'variant' think=0 pause=0 log=0 sent=0
9248 <second: # 'level' think=0 pause=0 log=0 sent=0
9248 <second: # 'post' think=0 pause=0 log=0 sent=0
9248 <second: # 'hard' think=0 pause=0 log=0 sent=0
9248 <second: # 'easy' think=0 pause=0 log=0 sent=0
9248 <second: # 'ping' think=0 pause=0 log=0 sent=0
9248 <second: pong 1
9248 <second: # 'force' think=0 pause=0 log=0 sent=0
9259 <second: # engine said: 
9259 <second: # engine said: 
9259 <second: # engine said: 9  r n b a k a b n r 
9259 <second: # engine said: 8  . . . . . . . . . 
9259 <second: # engine said: 7  . c . . . . . c . 
9259 <second: # engine said: 6  p . p . p . p . p 
9259 <second: # engine said: 5  . . . . . . . . . 
9259 <second: # engine said: 4  . . . . . . . . . 
9259 <second: # engine said: 3  P . P . P . P . P 
9259 <second: # engine said: 2  . C . . . . . C . 
9259 <second: # engine said: 1  . . . . . . . . . 
9259 <second: # engine said: 0  R N B A K A B N R 
9259 <second: # engine said: 
9259 <second: # engine said:    a b c d e f g h i
9259 <second: # engine said: 
9259 <second: # engine said:    side:           r
9259 <second: # engine said:    hash key:      -1866463866
9259 <second: # engine said:    king squares:  [e0, e9]
9259 <second: # engine said: 
New game (0): WukongJS 1.0 (UCI2WB)-WukongJS 1.0 (UCI2WB) (w)
9259 >first : computer
9259 >first : name WukongJS 1.0 (UCI2WB)
9259 >second: computer
9259 >second: name WukongJS 1.0 (UCI2WB)
9260 >first : black
9260 >first : time 30000
9260 >first : otim 30000
9260 >first : white
book hit = (NULL)
9260 >first : go
nps: w=-1, b=-1
9271 <first : # 'computer' think=0 pause=0 log=0 sent=0
9271 <first : # 'name' think=0 pause=0 log=0 sent=0
9271 <first : # 'black' think=0 pause=0 log=0 sent=0
9271 <first : # 'time' think=0 pause=0 log=0 sent=0
9271 <first : # 'otim' think=0 pause=0 log=0 sent=0
9271 <first : # 'white' think=0 pause=0 log=0 sent=0
9271 <first : # 'go' think=0 pause=0 log=0 sent=0
9271 <first : # start search
9271 <first : # fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r moves
9271 <first : # go btime 300000 wtime 300000 movestogo 40
9271 <first : # engine said: time: -1 inc 0 start 1611944187853 stop 0 depth 64 timeset 0
9271 <second: # 'computer' think=0 pause=0 log=0 sent=0
9271 <second: # 'name' think=0 pause=0 log=0 sent=0
9370 <first : # engine said: info score cp 12 depth 1 nodes 137 time 106 pv b0c2 
9370 <first :   1      12     10        137 b0c2 
9531 <first : # engine said: info score cp 0 depth 2 nodes 1321 time 267 pv b0c2 b9c7 
9531 <first :   2       0     26       1321 b0c2 b9c7 
9849 <first : # engine said: info score cp 12 depth 3 nodes 7497 time 585 pv b0c2 b9c7 h0g2 
9849 <first :   3      12     58       7497 b0c2 b9c7 h0g2 
10192 <first : # engine said: info score cp 0 depth 4 nodes 23744 time 928 pv h2c2 b7c7 c2c6 c7c3 
10192 <first :   4       0     92      23744 h2c2 b7c7 c2c6 c7c3 
10593 <first : # engine said: info score cp 12 depth 5 nodes 45899 time 1328 pv h2c2 b7c7 h0g2 h9g7 i0h0 
10593 <first :   5      12    132      45899 h2c2 b7c7 h0g2 h9g7 i0h0 
12806 <first : # engine said: info score cp 0 depth 6 nodes 221331 time 3541 pv h2e2 h9g7 b2c2 b7e7 h0g2 b9a7 
12806 <first :   6       0    354     221331 h2e2 h9g7 b2c2 b7e7 h0g2 b9a7 
14661 <first : # engine said: info score cp 10 depth 7 nodes 354300 time 5397 pv h2e2 h9g7 b2c2 b7e7 h0g2 h7h3 g3g4 h3c3 c2c6 
14661 <first :   7      10    539     354300 h2e2 h9g7 b2c2 b7e7 h0g2 h7h3 g3g4 h3c3 c2c6 
14673 <first : # engine said: bestmove h2e2
14673 <first : move h2e2
14673 >second: time 30000
14673 >second: otim 29458
book hit = (NULL)
14673 >second: usermove 14673 >second: h2e2
14673 >second: black
14673 >second: go
14846 <first : 
14850 <second: # 'time' think=0 pause=0 log=0 sent=0
14850 <second: # 'otim' think=0 pause=0 log=0 sent=0
14850 <second: # 'usermove' think=0 pause=0 log=0 sent=0
14850 <second: # 'black' think=0 pause=0 log=0 sent=0
14850 <second: # 'go' think=0 pause=0 log=0 sent=0
14850 <second: # start search
14850 <second: # fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r moves h2e2
14850 <second: # go btime 300000 wtime 294580 movestogo 40
14850 <second: # engine said: time: -1 inc 0 start 1611944193264 stop 0 depth 64 timeset 0
14850 <second: # engine said: info score cp 12 depth 1 nodes 137 time 50 pv b0c2 
14850 <second:   1      12      5        137 b0c2 
14903 <second: # engine said: info score cp 0 depth 2 nodes 1321 time 213 pv b0c2 b9c7 
14903 <second:   2       0     21       1321 b0c2 b9c7 
15240 <second: # engine said: info score cp 12 depth 3 nodes 7497 time 566 pv b0c2 b9c7 h0g2 
15321 <second:   3      12     56       7497 b0c2 b9c7 h0g2 
15597 <second: # engine said: info score cp 0 depth 4 nodes 23744 time 922 pv h2c2 b7c7 c2c6 c7c3 
15597 <second:   4       0     92      23744 h2c2 b7c7 c2c6 c7c3 
15983 <second: # engine said: info score cp 12 depth 5 nodes 45899 time 1309 pv h2c2 b7c7 h0g2 h9g7 i0h0 
15983 <second:   5      12    130      45899 h2c2 b7c7 h0g2 h9g7 i0h0 
18313 <second: # engine said: info score cp 0 depth 6 nodes 221331 time 3639 pv h2e2 h9g7 b2c2 b7e7 h0g2 b9a7 
18314 <second:   6       0    363     221331 h2e2 h9g7 b2c2 b7e7 h0g2 b9a7 
20159 <second: # engine said: info score cp 10 depth 7 nodes 354300 time 5485 pv h2e2 h9g7 b2c2 b7e7 h0g2 h7h3 g3g4 h3c3 c2c6 
20159 <second:   7      10    548     354300 h2e2 h9g7 b2c2 b7e7 h0g2 h7h3 g3g4 h3c3 c2c6 
20171 <second: # engine said: bestmove h2e2
20171 <second: move h2e2
GameEnds(26, Xboard: Forfeit due to invalid move: h2e2 (h2e2 via `/, `/) res=24, 4)
GE(26, Xboard: Forfeit due to invalid move: h2e2 (h2e2 via `/, `/) res=24, 4) bare king k=16 color=0
20175 >first : result 1-0 {Xboard: Forfeit due to invalid move: h2e2 (h2e2 via `/, `/) res=24}
20175 >second: result 1-0 {Xboard: Forfeit due to invalid move: h2e2 (h2e2 via `/, `/) res=24}
20175 >first : quit
20184 >second: quit
GameEnds(26, Xboard: Forfeit due to invalid move: h2e2 (h2e2 via `/, `/) res=24, 2)
Indeed uci2wb uses exactly UCI mode
Timing issues is clear - uci2wb sends "go btime .. wtime ..." instead of arena style "go wtime ... btime ..." - this can be fixed easily
But there's a serious issue: every time new search starts it uses FEN to init the engine which drops TT completely for I reset TT every time fen is set because that's the case for a new game to occur.

if this is the exact command:
fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r moves h2e2

than it seems to be wrong, it should be:
position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r - - 0 1 moves h2e2

well at least that is how Arena GUI does. Maybe UCI protocol allows this notation.



Anyway, it's getting too complicated and even if I manage to run via uci2wb adapter still end user would never be doing that,
so I took a decision to implement xboard protocol native. I just check Richard's Vice series and get to the gist.

One question - will xboard engine run in winboard?
User avatar
hgm
Posts: 28354
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: Fri Jan 29, 2021 7:29 pm Here's the extended debug:
...

Indeed uci2wb uses exactly UCI mode
This looks like what we now call UCI-Cyclone dialect. Indeed this always uses 'fen', never 'startpos', and it omits the 'position' keyword. Also note that the side-to-move in the FEN is indicated as 'r'.

I don't know which version of UCI2WB you use here. I thought that the most recent one only uses the Cyclone dialect (even in Xiangqi) if explicitly started with a -c argument. Without any flag argument it would use what I consider orthodox UCI. Chinese UCI Xiangqi engines (in particular the commercial engine Cyclone, and the accompanying GUI) tend to use this dialect.
Timing issues is clear - uci2wb sends "go btime .. wtime ..." instead of arena style "go wtime ... btime ..." - this can be fixed easily
But there's a serious issue: every time new search starts it uses FEN to init the engine which drops TT completely for I reset TT every time fen is set because that's the case for a new game to occur.
You should only do that when receiving 'ucinewgame', not on 'position fen'. Or in any case not if the FEN is the same as the previous 'position' command.
if this is the exact command:
fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r moves h2e2

than it seems to be wrong, it should be:
position fen rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR r - - 0 1 moves h2e2

well at least that is how Arena GUI does. Maybe UCI protocol allows this notation.



Anyway, it's getting too complicated and even if I manage to run via uci2wb adapter still end user would never be doing that,
so I took a decision to implement xboard protocol native. I just check Richard's Vice series and get to the gist.

One question - will xboard engine run in winboard?
Sure. XBoard and WinBoard are the same. Only the 'front-end' code for communicating to with the OS is different.

I guess this could indeed be the simpler solution; The other way is an uphill battle, as it would require some very unusual thing, which virtually no one else does. (Like using orthodox UCI in Xiangqi, where even using the UCI-Cylcone dialect is relatively rare compared to using UCCI.)
User avatar
hgm
Posts: 28354
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 »

And something completely different, that might be a bit off topic at this stage: I just managed to finish a move generator for Janggi (Korean Chess). This is very similar to Xiangqi, in the sense that there is also location-dependent moving. The King and Advisors are also confined to the Palace area. Not the Elephants, though, which are 'elongated' versions of the Horse in Janggi (doing one more diagonal step, so they can be blocked on two squares). And Rooks and Cannons have extra diagonal moves inside the Palace. But the underlying principles are the same, and the move generator I came up with would also work very well for Xiangqi, with only some small modifications. And it actually is quite simple:

Code: Select all

int MoveGen(int stm)
{
  int piece = stm, first = msp;
  do {
    int dir, step, fromSqr = location[piece];
    if(fromSqr == CAPTURED) continue;
    dir = moveLists[firstDir[piece-16] + fromSqr];
    while((step = steps[dir++])) {
      int toSqr = fromSqr;
      int r = ((piece & 15) < 4 ? ranges[dir-1] : 1);
      if((piece & 0xE) == 2) {   // Cannon
        while(--r && !board[toSqr+=step]) {}    // scan to mount
        if(!r) continue;                        // no mount in range
        if((board[toSqr] & 0xE) == 2) continue; // cannot hop over Cannon or edge
      } else
      if((piece & 0xC) == 4) {   // H or E
        int bs = blockSqrs[step+1];
        if(board[fromSqr + bs]) continue;
        if(piece & 2 &&                         // Elephant
           board[fromSqr + blockSqrs[step]]) continue;
      }
      do {
        int victim = board[toSqr+=step];
        if(victim & stm) break;              // captures own
        if(victim) {                         // capture
          int vv = mvvCode[victim-16];       // MVV/LVA sort key
          if(vv == 250) return 0;            // captures King
          moveStack[--first] = toSqr | fromSqr << 8 | vv << 24;
          break;
        } else moveStack[msp++] = toSqr | fromSqr << 8;
      } while(--r);
    }
  } while(++piece & 15);
  return first;
}
Unlike the micro-Max and KingSlayer move generators it supposes a piece list (location[]), which keeps track of where each piece of a given player is. So that it doesn't need to scan the board in the outermost loop to find the pieces, but can scan through the (much smaller) piece list. But that is just a subtle detail.

To allow for the location-dependence of the moves, it tabulates the start of the move list not just by piece type, but also by square. This has an additional advantage that the move lists can omit moves in directions that would immediately fall off board. Which prevents a lot of time wastage. Slider moves will of course eventually fall off board, so there still has to be a rim of 'edge guards' around the board to stop them there.

The moves can also have a location dependency of their range: Rooks and Cannons can move diagonally inside the Palace, but then they cannot leave it, and must stop at the Palace boundary. So for them the range is tabulated squarewise too. All other pieces are leapers (albeit 'lame' leapers), and their range will be set to 1.

After retrieval of a move step and range for a certain direction, three piece types need special treatment: a Cannon would first have to find a piece to jump over. (In Janggi Cannons must always jump, also for non-captures. And they cannot jump each other!) If this is found, without completely exhausting the range, move generation continues from there as if it was a Rook. Horses and Elephants need to test whether the move (which is guaranteed to end on the board) are blocked. Which square(s) they can be blocked is looked up in a table indexed by the step they make to their final destination.

If these tests are successfully passed, there is the normal loop over distances, the number of iterations controlled by the range variable r. Of course an obstacle in the path would break out of the loop earlier. Generated moves are put in the moveStack[] array; depending on whether they are captures or non-captures at the beginning or end of the list. (That saves a lot of time on move sorting later.)

Of course most of the complexity is in the tables:

Code: Select all

unsigned char moveLists[] = { // start of move list in steps[], per square

//        white King, Advisor, Pawn               black King, Advisor, Pawn
  0,  0,  0, 28,  6, 34,  0,  0,  0,     42, 13, 13, 13, 13, 13, 13, 13, 48,
  0,  0,  0, 17,  1, 22,  0,  0,  0,     41, 12, 12, 12, 10, 12, 12, 12, 47,
  0,  0,  0, 40, 12, 46,  0,  0,  0,     41, 12, 12, 39, 12, 45, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6, 27,  6, 33,  6,  6, 35,      0,  0,  0, 28,  6, 34,  0,  0,  0,
 29,  6,  6,  6,  4,  6,  6,  6, 35,      0,  0,  0, 17,  1, 22,  0,  0,  0,
 30,  7,  7,  7,  7,  7,  7,  7, 36,      0,  0,  0, 40, 12, 46,  0,  0,  0,

//                            Horse                                 Elephant
 63, 62, 61, 61, 61, 61, 61, 86, 56,     113,113,112,111,111,111,136,106,106,
 91, 90, 59, 59, 59, 59, 59, 85, 55,     113,113,112,111,111,111,136,106,106,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     141,141,140,149,149,149,135,105,105,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     119,119,145,123,123,123,130,131,131,
 69, 95, 73, 73, 73, 73, 73, 80, 81,     120,120,146,125,125,125,126,127,127,
 70, 96, 75, 75, 75, 75, 75, 76, 77,     120,120,146,125,125,125,126,127,127,

 //                            Rook                                  Cannon
 29,  6,  6, 27,  6, 33,  6,  6, 35,     29, 29,  6, 27,  6, 33,  6, 35, 35,
 17, 16, 16, 16,  1, 16, 16, 16, 22,     29, 29,  6,  6,  6,  6,  6, 35, 35,
 17, 16, 16, 38, 16, 44, 16, 16, 22,     17, 17, 16, 38, 16, 44, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 26, 16, 32, 16, 16, 22,     17, 17, 16, 26, 16, 32, 16, 22, 22,
 17, 16, 16, 16,  1, 16, 16, 16, 22,     41, 41, 12, 12, 12, 12, 12, 47, 47,
 41, 12, 12, 39, 12, 45, 12, 12, 47,     41, 41, 12, 39, 12, 45, 12, 47, 47,
};

signed char rawBlock[] = {
  0,-18,-37,  0,  0,-18,-35,  0,       0,0,0,0,0,0,0,0,0,0,
 -1,-20,-18,-18,-18,-18,  1,-16,       0,0,0,0,0,0,0,0,0,0,
  0, -1, -1,  0,  0,  1,  1,  0,       0,0,0,0,0,0,0,0,0,0,
  0,  0,  0,  0,  0,  0,  0,  0,       0,0,0,0,0,0,0,0,0,0,
  0, -1, -1,  0,  0,  1,  1,  0,       0,0,0,0,0,0,0,0,0,0,
 -1, 16, 18, 18, 18, 18,  1, 20,       0,0,0,0,0,0,0,0,0,0,
  0, 18, 35,  0,  0, 18, 37
};
#define blockSqrs (rawBlock + (3*18 + 3))

signed char steps[] = {
// even when using sub-lists several lists are needed to suit all square types
/*
   0,
   // Queen
   17, 19, 18,  1, -1, 0,             // 1
  -17,-19,-18,  1, -1, 0,             // 7
   -1,  1, 18,-18, 0,                 // 13
    1, -1,-18, 18, 0,                 // 18
   17,-19,-17,                        // 23
*/
    0,
   // Queen
  -18,-19,-17,                        // 1
   17, 19, 18,  1, -1, 0,             // 4
  -17,-19,-18,  1, -1, 0,             // 10
   -1,  1, 18,-18, 0,                 // 16
    1, -1,-18, 18, 0,                 // 21
  -18, -1, 19, 18,  1, 0,             // 26
  -18,  1, 17, 18, -1, 0,             // 32
   18, -1,-17,-18,  1, 0,             // 38
   18,  1,-19,-18, -1, 0,             // 44
   // Horse
  -16, 20, 37,-35,-37,-20, 35, 16, 0, // 50
  -20,-16, 16, 35, 37, 20, 0,         // 59
   35,-37, 37, 20,-35,-16, 0,         // 66
   16, 20,-16,-35,-37,-20, 0,         // 73
  -35,-37,-20, 16, 0,                 // 80
  -20, 35, 37, 16, 0,                 // 85
   35, 37, 20,-16, 0,                 // 90
   20,-37,-35,-16, 0,                 // 95
   // Elephant
  -33, 39, 56,-52,-56,-39, 52, 33, 0, // 100
  -39,-33, 33, 52, 56, 39, 0,         // 109
   52,-56, 56, 39,-52,-33, 0,         // 116
   33, 39,-33,-52,-56,-39, 0,         // 123
  -52,-56,-39, 33, 0,                 // 130
  -39, 52, 56, 33, 0,                 // 135
   35, 56, 39,-33, 0,                 // 140
   39,-56,-52,-33, 0,                 // 145
};

char ranges[] = {
  0,
  1, 1, 1,
  1, 1, 9, 8, 8, 0,
  1, 1, 9, 8, 8, 0,
  8, 8, 8, 8, 0,
  8, 8, 8, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0
};

int firstDir[] = {
// R   R   C   C   H   H   E   E   A   A   K   P   P   P   P   P
 360,360,369,369,180,180,189,189,  0,  0,  0,  0,  0,  0,  0,  0,
 360,360,369,369,180,180,189,189,  9,  9,  9,  9,  9,  9,  9,  9
};

unsigned char mvvCode[] = {
 200,200,150,150,100,100, 80, 80, 40, 40,250, 20, 20, 20, 20, 20,
 200,200,150,150,100,100, 80, 80, 40, 40,250, 20, 20, 20, 20, 20
};

int moveStack[10000];
User avatar
maksimKorzh
Posts: 775
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: Fri Jan 29, 2021 9:57 pm And something completely different, that might be a bit off topic at this stage: I just managed to finish a move generator for Janggi (Korean Chess). This is very similar to Xiangqi, in the sense that there is also location-dependent moving. The King and Advisors are also confined to the Palace area. Not the Elephants, though, which are 'elongated' versions of the Horse in Janggi (doing one more diagonal step, so they can be blocked on two squares). And Rooks and Cannons have extra diagonal moves inside the Palace. But the underlying principles are the same, and the move generator I came up with would also work very well for Xiangqi, with only some small modifications. And it actually is quite simple:

Code: Select all

int MoveGen(int stm)
{
  int piece = stm, first = msp;
  do {
    int dir, step, fromSqr = location[piece];
    if(fromSqr == CAPTURED) continue;
    dir = moveLists[firstDir[piece-16] + fromSqr];
    while((step = steps[dir++])) {
      int toSqr = fromSqr;
      int r = ((piece & 15) < 4 ? ranges[dir-1] : 1);
      if((piece & 0xE) == 2) {   // Cannon
        while(--r && !board[toSqr+=step]) {}    // scan to mount
        if(!r) continue;                        // no mount in range
        if((board[toSqr] & 0xE) == 2) continue; // cannot hop over Cannon or edge
      } else
      if((piece & 0xC) == 4) {   // H or E
        int bs = blockSqrs[step+1];
        if(board[fromSqr + bs]) continue;
        if(piece & 2 &&                         // Elephant
           board[fromSqr + blockSqrs[step]]) continue;
      }
      do {
        int victim = board[toSqr+=step];
        if(victim & stm) break;              // captures own
        if(victim) {                         // capture
          int vv = mvvCode[victim-16];       // MVV/LVA sort key
          if(vv == 250) return 0;            // captures King
          moveStack[--first] = toSqr | fromSqr << 8 | vv << 24;
          break;
        } else moveStack[msp++] = toSqr | fromSqr << 8;
      } while(--r);
    }
  } while(++piece & 15);
  return first;
}
Unlike the micro-Max and KingSlayer move generators it supposes a piece list (location[]), which keeps track of where each piece of a given player is. So that it doesn't need to scan the board in the outermost loop to find the pieces, but can scan through the (much smaller) piece list. But that is just a subtle detail.

To allow for the location-dependence of the moves, it tabulates the start of the move list not just by piece type, but also by square. This has an additional advantage that the move lists can omit moves in directions that would immediately fall off board. Which prevents a lot of time wastage. Slider moves will of course eventually fall off board, so there still has to be a rim of 'edge guards' around the board to stop them there.

The moves can also have a location dependency of their range: Rooks and Cannons can move diagonally inside the Palace, but then they cannot leave it, and must stop at the Palace boundary. So for them the range is tabulated squarewise too. All other pieces are leapers (albeit 'lame' leapers), and their range will be set to 1.

After retrieval of a move step and range for a certain direction, three piece types need special treatment: a Cannon would first have to find a piece to jump over. (In Janggi Cannons must always jump, also for non-captures. And they cannot jump each other!) If this is found, without completely exhausting the range, move generation continues from there as if it was a Rook. Horses and Elephants need to test whether the move (which is guaranteed to end on the board) are blocked. Which square(s) they can be blocked is looked up in a table indexed by the step they make to their final destination.

If these tests are successfully passed, there is the normal loop over distances, the number of iterations controlled by the range variable r. Of course an obstacle in the path would break out of the loop earlier. Generated moves are put in the moveStack[] array; depending on whether they are captures or non-captures at the beginning or end of the list. (That saves a lot of time on move sorting later.)

Of course most of the complexity is in the tables:

Code: Select all

unsigned char moveLists[] = { // start of move list in steps[], per square

//        white King, Advisor, Pawn               black King, Advisor, Pawn
  0,  0,  0, 28,  6, 34,  0,  0,  0,     42, 13, 13, 13, 13, 13, 13, 13, 48,
  0,  0,  0, 17,  1, 22,  0,  0,  0,     41, 12, 12, 12, 10, 12, 12, 12, 47,
  0,  0,  0, 40, 12, 46,  0,  0,  0,     41, 12, 12, 39, 12, 45, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6,  6,  6,  6,  6,  6, 35,     41, 12, 12, 12, 12, 12, 12, 12, 47,
 29,  6,  6, 27,  6, 33,  6,  6, 35,      0,  0,  0, 28,  6, 34,  0,  0,  0,
 29,  6,  6,  6,  4,  6,  6,  6, 35,      0,  0,  0, 17,  1, 22,  0,  0,  0,
 30,  7,  7,  7,  7,  7,  7,  7, 36,      0,  0,  0, 40, 12, 46,  0,  0,  0,

//                            Horse                                 Elephant
 63, 62, 61, 61, 61, 61, 61, 86, 56,     113,113,112,111,111,111,136,106,106,
 91, 90, 59, 59, 59, 59, 59, 85, 55,     113,113,112,111,111,111,136,106,106,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     141,141,140,149,149,149,135,105,105,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     118,118,116,100,100,100,102,104,104,
 68, 66, 50, 50, 50, 50, 50, 52, 54,     119,119,145,123,123,123,130,131,131,
 69, 95, 73, 73, 73, 73, 73, 80, 81,     120,120,146,125,125,125,126,127,127,
 70, 96, 75, 75, 75, 75, 75, 76, 77,     120,120,146,125,125,125,126,127,127,

 //                            Rook                                  Cannon
 29,  6,  6, 27,  6, 33,  6,  6, 35,     29, 29,  6, 27,  6, 33,  6, 35, 35,
 17, 16, 16, 16,  1, 16, 16, 16, 22,     29, 29,  6,  6,  6,  6,  6, 35, 35,
 17, 16, 16, 38, 16, 44, 16, 16, 22,     17, 17, 16, 38, 16, 44, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 16, 16, 16, 16, 16, 22,     17, 17, 16, 16, 16, 16, 16, 22, 22,
 17, 16, 16, 26, 16, 32, 16, 16, 22,     17, 17, 16, 26, 16, 32, 16, 22, 22,
 17, 16, 16, 16,  1, 16, 16, 16, 22,     41, 41, 12, 12, 12, 12, 12, 47, 47,
 41, 12, 12, 39, 12, 45, 12, 12, 47,     41, 41, 12, 39, 12, 45, 12, 47, 47,
};

signed char rawBlock[] = {
  0,-18,-37,  0,  0,-18,-35,  0,       0,0,0,0,0,0,0,0,0,0,
 -1,-20,-18,-18,-18,-18,  1,-16,       0,0,0,0,0,0,0,0,0,0,
  0, -1, -1,  0,  0,  1,  1,  0,       0,0,0,0,0,0,0,0,0,0,
  0,  0,  0,  0,  0,  0,  0,  0,       0,0,0,0,0,0,0,0,0,0,
  0, -1, -1,  0,  0,  1,  1,  0,       0,0,0,0,0,0,0,0,0,0,
 -1, 16, 18, 18, 18, 18,  1, 20,       0,0,0,0,0,0,0,0,0,0,
  0, 18, 35,  0,  0, 18, 37
};
#define blockSqrs (rawBlock + (3*18 + 3))

signed char steps[] = {
// even when using sub-lists several lists are needed to suit all square types
/*
   0,
   // Queen
   17, 19, 18,  1, -1, 0,             // 1
  -17,-19,-18,  1, -1, 0,             // 7
   -1,  1, 18,-18, 0,                 // 13
    1, -1,-18, 18, 0,                 // 18
   17,-19,-17,                        // 23
*/
    0,
   // Queen
  -18,-19,-17,                        // 1
   17, 19, 18,  1, -1, 0,             // 4
  -17,-19,-18,  1, -1, 0,             // 10
   -1,  1, 18,-18, 0,                 // 16
    1, -1,-18, 18, 0,                 // 21
  -18, -1, 19, 18,  1, 0,             // 26
  -18,  1, 17, 18, -1, 0,             // 32
   18, -1,-17,-18,  1, 0,             // 38
   18,  1,-19,-18, -1, 0,             // 44
   // Horse
  -16, 20, 37,-35,-37,-20, 35, 16, 0, // 50
  -20,-16, 16, 35, 37, 20, 0,         // 59
   35,-37, 37, 20,-35,-16, 0,         // 66
   16, 20,-16,-35,-37,-20, 0,         // 73
  -35,-37,-20, 16, 0,                 // 80
  -20, 35, 37, 16, 0,                 // 85
   35, 37, 20,-16, 0,                 // 90
   20,-37,-35,-16, 0,                 // 95
   // Elephant
  -33, 39, 56,-52,-56,-39, 52, 33, 0, // 100
  -39,-33, 33, 52, 56, 39, 0,         // 109
   52,-56, 56, 39,-52,-33, 0,         // 116
   33, 39,-33,-52,-56,-39, 0,         // 123
  -52,-56,-39, 33, 0,                 // 130
  -39, 52, 56, 33, 0,                 // 135
   35, 56, 39,-33, 0,                 // 140
   39,-56,-52,-33, 0,                 // 145
};

char ranges[] = {
  0,
  1, 1, 1,
  1, 1, 9, 8, 8, 0,
  1, 1, 9, 8, 8, 0,
  8, 8, 8, 8, 0,
  8, 8, 8, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0,
  7, 3, 2, 9, 8, 0
};

int firstDir[] = {
// R   R   C   C   H   H   E   E   A   A   K   P   P   P   P   P
 360,360,369,369,180,180,189,189,  0,  0,  0,  0,  0,  0,  0,  0,
 360,360,369,369,180,180,189,189,  9,  9,  9,  9,  9,  9,  9,  9
};

unsigned char mvvCode[] = {
 200,200,150,150,100,100, 80, 80, 40, 40,250, 20, 20, 20, 20, 20,
 200,200,150,150,100,100, 80, 80, 40, 40,250, 20, 20, 20, 20, 20
};

int moveStack[10000];
Congrats! This is really cool.
I've started playing around with winboard (implementing it) but that's so against the stateless approach - that depresses me every time I read a new issue in debug file. I guess it would be easier to make this cyclone dialect.
User avatar
hgm
Posts: 28354
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 »

Supporting the Cyclone dialect if you already do orthodox UCI might indeed not be that difficult. All you have to do is understand r as w in the FEN parser, ignore 'position', and let 'fen' and 'startpos' do what "position fen" and "position startpos" used to do before. Then you would understand both dialects automatically. UCCI is a much tougher cookie; almost everything is different there. (It has for instance no 'name' and 'value' keywords, uses 'time' and 'oppotime' instead of 'wtime' and 'btime', with subtly different meaning for their values, etc.)

And of course you should not clear the hash table on receiving a FEN; this was bad practice anyway, even for orthodox UCI. There isn't even a real need to do it on 'ucinewgame'; as long as you are not playing another variant, the hash table left over from a previous game should also be valid for this one. Likely there isn't anything in there that you can use, but it should never hurt.