资源
代码
快速开始
创建棋盘只需要 xiangqiboard.js,从源码下载地址下载源码并导入网页中:
<link rel="stylesheet" type="text/css" href="css/xiangqiboard-0.3.3.css">
<script src="js/xiangqiboard-0.3.3.min.js"></script>在本博客中,为了增加代码的复用性,在特定地址中放置图片资源的位置,config 中设定好对应的位置:
const test_config = {
boardTheme: '../../../../js/xiangqiboard/img/xiangqiboards/wikimedia/xiangqiboard.svg',
pieceTheme: '../../../../js/xiangqiboard/img/xiangqipieces/wikimedia/{piece}.svg',
position: 'start'
};创建棋盘:
<div id="board1" style="margin: 0 auto; width: min(450px, 100%); overflow: auto;"></div>
<script>const board1 = Xiangqiboard('board1', test_config)</script>棋谱展示
圣光将赐予我胜利!
实现细节
从 源码下载地址 引入 xiangqi.js。
<script src="/js/xiangqi.js"></script>添加高亮 CSS:
.highlight-red {
box-shadow: inset 0 0 3px 3px yellow;
}
.highlight-black {
box-shadow: inset 0 0 3px 3px blue;
}画一个棋盘。写一个 board.js 用于控制棋盘!再写点辅助工具!
<div id="board" style="margin: 0 auto; width: min(450px, 100%); overflow: auto;"></div>
<div id="board-tool" style="margin: 10px auto 5px; width: 450px; display: flex;">
<button id="start">初局</button>
<button id="prev">前一步</button>
<p id="description">圣光将赐予我胜利!</p>
<button id="next">后一步</button>
<button id="end">终局</button>
</div>
<script src="board.js"></script>board.js 下的内容:
let board = null;
let $board = $('#board');
let squareClass = 'square-2b8ce'
let colorToHighlight = null;
let highlightChess = true;
let squareToHighlight = null;
var $boardTool = $("#board-tool");
var $prevButton = $boardTool.find("#prev");
var $description = $boardTool.find("#description");
var $nextButton = $boardTool.find("#next");
var $startButton = $boardTool.find("#start");
var $endButton = $boardTool.find("#end");
var totalSteps = 0;
var game = new Xiangqi();
const pgn = ['[Game "Chinese Chess"]',
'[Event "Test"]',
'[Date "2024.04.16"]',
'[Red "A"]',
'[Black "B"]',
'[Result "1/2-1/2"]',
'[Format "ICCS"]',
'',
'1. b2e2 b9c7',
'2. b0c2 a9b9',
'3. a0b0h9g7',
'4. g3g4 c6c5',
'5. h0g2h7h3',
'6. e3e4 b7b3',
'7. i0i1h3g3',
'8. g0i2 i9h9',
'9. i1f1g3d3',
'10. f1f7 d3d2',
'11. g2f4 d2h2',
'12. f4e6 c7e6',
'13. e2e6 b9b6',
'14. e6e5 e9e8',
'15. f7g7h9h3',
'16. g7g9']
function initXiangqi() {
console.log("载入 PGN:" + game.load_pgn(pgn.join('\n')), game.header());
totalSteps = game.history().length;
do {
flag = game.undo();
} while (flag);
}
function initXiangqiboard() {
function onMoveEnd() {
if (highlightChess)
{
$board.find('.square-' + squareToHighlight)
.addClass('highlight-' + colorToHighlight);
}
}
let config = {
boardTheme: '../../../../js/xiangqiboard/img/xiangqiboards/wikimedia/xiangqiboard.svg',
pieceTheme: '../../../../js/xiangqiboard/img/xiangqipieces/wikimedia/{piece}.svg',
position: game.fen(),
draggable: true,
onMoveEnd: onMoveEnd
};
board = Xiangqiboard($board, config);
}
function initXiangqiboardTool() {
$startButton.click(function () {
console.log("初始按钮被点击了");
do {
move = game.undo();
removehighlight(move);
} while (move);
board.position(game.fen());
$description.text(game.history().length.toString() + "/" + totalSteps.toString());
});
$endButton.click(function () {
console.log("终局按钮被点击了");
do {
move = game.redo();
removehighlight(move);
} while (move);
board.position(game.fen());
$description.text(game.history().length.toString() + "/" + totalSteps.toString());
});
$prevButton.click(function () {
console.log("前一步按钮被点击了");
move = game.undo();
highlight(move);
board.position(game.fen());
$description.text(game.history().length.toString() + "/" + totalSteps.toString());
});
$nextButton.click(function () {
console.log("后一步按钮被点击了");
move = game.redo();
highlight(move);
board.position(game.fen());
$description.text(game.history().length.toString() + "/" + totalSteps.toString());
});
function removehighlight(move){
console.log(move);
$board.find('.' + squareClass).removeClass('highlight-red');
$board.find('.' + squareClass).removeClass('highlight-black');
highlightChess = false;
}
function highlight(move) {
console.log(move);
if (move) {
if (move.color === 'r') {
$board.find('.' + squareClass).removeClass('highlight-red');
$board.find('.square-' + move.from).addClass('highlight-red');
squareToHighlight = move.to;
colorToHighlight = 'red';
} else {
$board.find('.' + squareClass).removeClass('highlight-black');
$board.find('.square-' + move.from).addClass('highlight-black');
squareToHighlight = move.to;
colorToHighlight = 'black';
}
highlightChess = true;
}
}
}
$(document).ready(function () {
initXiangqi();
initXiangqiboard();
initXiangqiboardTool();
});initXiangqi();初始化象棋走棋逻辑。initXiangqiboard();初始化象棋棋盘逻辑。initXiangqiboardTool();初始化象棋辅助工具逻辑。
处理 PGN 格式
xiangqi.js 的 load_pgn() 对 PGN 读取形式似乎有些不同:
- 不支持
Chinese格式。 - 对
ICCS格式的支持很奇怪,感觉是从国际象棋里搬过来的? - 不会读取 PGN 里的
[Fen],好像只支持初始界面……
直接读取象棋巫师生成的或是其它地方下载的 PGN 棋谱将会失败。得用 Python-Chinese-Chess 处理一番!
对于象棋巫师瞎下的 XXX.PGN:
[Game "Chinese Chess"]
[Event ""]
[Round ""]
[Date ""]
[Site ""]
[RedTeam ""]
[Red ""]
[BlackTeam ""]
[Black ""]
[Result "*"]
[ECCO "C04"]
[Opening "中炮七路马对屏风马"]
[Variation "红进中兵对黑双炮过河"]
1. 炮八平五 马2进3
2. 马八进七 车1平2
3. 车九平八 马8进7
4. 兵三进一 卒3进1
5. 马二进三 炮8进4
6. 兵五进一 炮2进4
7. 车一进一 炮8平7
8. 相三进一 车9平8
9. 车一平四 炮7平4
10. 车四进六 炮4进1
11. 马三进四 炮4平8
12. 马四进五 马3进5
13. 炮五进四 车2进3
14. 炮五退一 将5进1
15. 车四平三 车8进6
16. 车三进二 *
======================
欢迎访问象棋百科全书网
推荐用象棋巫师观赏棋谱
http://www.xqbase.com/
将上面的 PGN 文件转成 xiangqi.js 所识别的格式的处理代码:
import cchess
board = cchess.Board.from_pgn("XXX.PGN") # 象棋巫师导出的 PGN 格式
# 头文件,自己改
pgn = """[Game "Chinese Chess"]
[Event "Test"]
[Date "2024.04.16"]
[Red "A"]
[Black "B"]
[Result "*"]
[Format "ICCS"]
"""
for s in board.to_pgn(format="ICCS").split('\n'):
if(s[0:4] == '[Gen'):
start_fen = s.split(' ')[1][1:]
elif(s[0] != '['):
temp = '. '.join(s.lower().replace("-", "").split('.'))
pgn += temp + '\n'
pgn.split('\n')[:-1]Jupyter Notebook 输出:
['[Game "Chinese Chess"]',
'[Event "Test"]',
'[Date "2024.04.16"]',
'[Red "A"]',
'[Black "B"]',
'[Result "1/2-1/2"]',
'[Format "ICCS"]',
'',
'1. b2e2 b9c7',
'2. b0c2 a9b9',
'3. a0b0 h9g7',
'4. g3g4 c6c5',
'5. h0g2 h7h3',
'6. e3e4 b7b3',
'7. i0i1 h3g3',
'8. g0i2 i9h9',
'9. i1f1 g3d3',
'10. f1f7 d3d2',
'11. g2f4 d2h2',
'12. f4e6 c7e6',
'13. e2e6 b9b6',
'14. e6e5 e9e8',
'15. f7g7 h9h3',
'16. g7g9']