npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zh-chess

v3.2.1

Published

A Chinese chess framework that supports browsers and nodejs(一款运行在浏览器和nodejs环境的中国象棋框架)

Readme

ZhChess(中国象棋)

GitHub star GitHub fork Gitee star Gitee fork

MIT开源协议

一款JavaScript语言编写的中国象棋游戏框架,支持nodejs,浏览器,vue,react等前端框架。(支持typescript)更多象棋知识

案例网站

更新日志

v3.2.0

  • 新增扩展框架ChessPlugin / ChessRenderer / ChessVariant):可自定义棋盘与棋子外观、变体规则(如揭棋骨架),默认中国象棋行为不变。
  • 新增文字棋盘导出:exportTextBoard(pieces, options) 与实例方法 game.exportTextBoard(options),支持 chinese / ascii 与自定义 formatCell
  • 新增 game.use(plugin) / game.unuse(name) / game.getDrawLayout()GameInfo 支持 plugins / renderer / variant
  • 示例见 example/framework-demo.js(文字导出、方形棋子、揭棋钩子骨架)。

v3.1.0

  • 新增 generateMoves(side) 批量走法生成 API,作为 AI 搜索入口(内部仅构建一次棋盘位表并交由本方棋子复用,比逐个调用 getMovePoints(pl) 更高效)。
  • 新增 generateLegalMoves(side) 合法走法生成 API:对每个伪合法走法做送将过滤(checkGeneralInTrouble 增量模拟),返回扁平化 Move[]{ from, to, captured }),AI 搜索可直接使用。
  • 新增 isLegalMove(side, from, to) 单步合法性判定(含送将过滤)。
  • 新增 getPiecesOfSide(side) 获取指定方存活棋子列表。
  • 新增 isGameOver(),旧 gameOver() 保留为 @deprecated 委托。
  • checkGeneralInTrouble 由 protected 改为 public,便于引擎侧复用判定逻辑。
  • 事件 API 由 5 个重载改为泛型签名 on<K extends GameEventName>(e: K, fn: GameEventMap[K]),类型更严格。
  • 修正历史类型拼写错误:PiecePosInfo(原 PeicePosInfo)、GamePieceGridDiffX/YPENPieceNameCode(原 PENPeiceNameCode)、UpdateSuccess(原 updateSuccess),旧名保留为 @deprecated 别名以兼容既有代码。
  • 项目工程化:新增 ESLint / Prettier 配置与 GitHub Actions CI(install / lint / format / build / test),清理存量 lint 告警。
  • 等价性测试黄金快照化,新增 generateMoves 与逐子 getMovePoints 的一致性断言;新增 assertLegalMovesConsistency 校验 generateLegalMoves / isLegalMoveupdate 逐走法判定的一致性。

v3.0.0

  • 重写走法生成与合法性校验,性能与正确性提升。
  • 优化将军判定:按精确 constructor 判断棋子类型,避免继承链误判。
  • 棋盘位表(Board)在走法生成与将军检测间复用,减少重复构建。
  • 等价性回归测试固定为 v2.1.1 行为,保证重构后规则完全一致。

安装使用

nodejs

  1. 项目新增依赖(需要借助canvas来显示游戏图片)
D:your project>npm i zh-chess canvas -D # or cnpm i zh-chess canvas -D
  1. 直接导入使用
const {
    createCanvas
} = require('canvas')
const CTX_WIDTH = 375,
    CTX_HEIGHT = 375
const canvas = createCanvas(CTX_WIDTH, CTX_HEIGHT)
const ctx = canvas.getContext('2d')
const ZhChess = require("zh-chess").default
const fs = require('fs')
const out = fs.createWriteStream('./test.jpg') // 创建文件流

const game = new ZhChess({
    ctx, // ctx 属性可传 可不传 只要 游戏不需要更新画面 光靠 游戏逻辑来运行完全可以足够
    gameWidth: CTX_WIDTH,
    gameHeight: CTX_HEIGHT
})
game.gameStart("RED") // 开始游戏
game.moveStr("炮2平5", "RED") // 移动棋子
game.draw(ctx) // 更新画布 需要传入画布
const stream = canvas.createJPEGStream()
stream.pipe(out) // 写入文件
out.on('finish', () => console.log('The JPEG file was created.'))

浏览器环境

  1. 下载源码
git clone https://github.com/kongyijilafumi/zh-chess
# or 国内码云
git clone https://gitee.com/kong_yiji_and_lavmi/zh-chess
  1. 在html的 head 部分使用 script 标签引入 ./lib/zh-chess.browser.min.js 文件
<head>
    <script script="./lib/zh-chess.browser.min.js"></script>
</head>
  1. 引入之后的下方,新建 script 标签,直接调用api即可。
<head>
    <script script="./lib/zh-chess.browser.min.js"></script>
</head>

<body>
    <canvas id="canvas" height="375" width="375" style="width:375px;height:375px;"></canvas>
</body>
<script>
    const app = document.getElementById("canvas")
    const ctx = app.getContext("2d")
    const zhchess = new ZhChess.default({
        ctx,
        gameWidth: 375,
        gameHeight: 375
    })
    // 绑定点击事件
    app.addEventListener("click", zhchess.listenClickAsync, false)
</script>

react, vue等前端构建项目使用

  • 安装依赖
your project>npm i zh-chess -D #or cnpm i zh-chess 
  • vue2.x项目中使用
<template>
    <canvas ref="canvas" height="375" width="375" style="width: 375px; height: 375px;" />
</template>
<script>
    import ZhChess from "zh-chess";
    export default {
        data() {
            return {
                game: null
            }
        },
        mounted() {
            const canvas = this.$refs.canvas;
            const ctx = canvas.getContext("2d");
            this.game = new ZhChess({
                ctx,
                gameHeight: 375,
                gameWidth: 375,
                duration: 200, // 默认200 ms单位 值棋子运动时长
            });
            this.game.gameStart("RED"); // 以红方开始游戏
            canvas.addEventListener("click", this.game.listenClickAsync, false);
        },
    }
</script>
  • vue3.x项目中使用
<template>
    <canvas ref="canvas" height="375" width="375" style="width: 375px; height: 375px;" />
</template>
<script lang="ts" setup>
    import ZhChess, {
        GameOverCallback,
        MoveCallback,
        MoveFailCallback,
    } from "zh-chess";

    import {
        onMounted,
        reactive,
        ref,
        getCurrentInstance
    } from "vue";

    const page = getCurrentInstance();
    const CANVAS_WIDTH = 375;
    const CANVAS_HEIGHT = 375;

    let game = reactive({}
        as ZhChess);
    const onOver: GameOverCallback = (winnerSide) => {
        console.log(`${winnerSide}胜`);
    };
    // 挂载
    onMounted(() => {
        const canvas = page?.refs.canvas as HTMLCanvasElement;
        game = new ZhChess({
            ctx: canvas.getContext("2d") as CanvasRenderingContext2D,
            gameHeight: CANVAS_WIDTH,
            gameWidth: CANVAS_HEIGHT,
        });
        game.gameStart("RED");
        canvas.addEventListener("click", game.listenClickAsync, false);
        game.on("over", onOver);
    });
</script>
  • react项目中使用
import ZhChess from "zh-chess"
import { useEffect, useRef, useState } from "react";
export default function App() {
  const [gameInstance, setGame] = useState<ZhChess | null>(null)
  const canvas = useRef<HTMLCanvasElement>(null)
  useEffect(() => {
    if (canvas.current) {
      const CTX_WIDTH = 375, CTX_HEIGHT = 375,ctx = canvas.current.getContext('2d') as CanvasRenderingContext2D;
      const game = new ZhChess({
      ctx,
      gameHeight: CTX_HEIGHT,
      gameWidth: CTX_WIDTH
  })
      game.gameStart("RED")
    canvas.current.addEventListener("click", game.listenClickAsync, false)
      setGame(game)
    }
  }, [canvas])
  return <div className="app">
    <canvas ref={canvas} width="375" height="375" style={{width:375,height:375}} />
  </div>
} 

接口

GitHub文档地址 | Gitee文档地址

ZhChess 游戏类

constructor(obj: GameInfo);

GameInfo Properties

  • blackPeiceBackground?: string 黑棋子背景色 默认:#fdec9e
  • checkerboardBackground?: string 棋盘背景色 默认:#faebd7
  • ctx?: CanvasRenderingContext2D 画布

如果不需要游戏显示出来,可以不需要 画布

  • drawMovePoint?: boolean 选中是否绘画可移动的点 默认: true
  • duration?: number 棋子运动速度时长 毫秒单位 默认:200
  • gameHeight?: number 游戏窗口高度大小 默认:800
  • gamePadding?: number 游戏内边距大小距离棋盘 默认:20
  • gameWidth?: number 游戏窗口宽度大小 默认:800
  • movePointColor?: string 绘画可移动点的颜色 默认:#25dd2a
  • redPeiceBackground?: string 红棋子背景色 默认:#feeca0
  • scaleRatio?: number 画布缩放大小 默认:1 (用于移动端,解决画布模糊问题)

Properties

duration: number

Accessors

棋子运动速度时长 毫秒单位

get currentGameSide(): null | PieceSide

获取游戏方

get currentLivePieceList(): PieceList

获取当前存活的棋子列表

get currentRadius(): number

获取当前象棋绘制半径

get winnerSide(): null | PieceSide

获取赢棋方

gameStart(side: PieceSide): void

选择玩家方并且初始化游戏,side只能是 RED | BLACK .

  const game = new ZhChess({
      gameHeight: 375,
      gameWidth: 375
  })
  game.gameStart("RED")

Methods

changeCurrentPlaySide(side: PieceSide): void

更改当前走棋方

changePlaySide(side: PieceSide): void

更换玩家视角

checkDraw(): void

检查是否有画布 有会根据当前棋子位置状态去更新画布 否则不更新 报出错误

checkGameState(): MoveResult

棋子运动前检查游戏状态是否可以运动

draw(ctx: CanvasRenderingContext2D): void

根据当前棋子状态绘画 棋盘状态 游戏数据 画出布局

isGameOver(): boolean

游戏是否结束

gameOver(): boolean

游戏是否结束(@deprecated 请使用 isGameOver()

gameStart(side: PieceSide): void

初始化选择玩家方 初始化棋盘

generateMoves(side: PieceSide): MovePointList[]

批量生成指定方所有存活棋子的走法列表(AI 搜索入口)。内部仅构建一次棋盘位表并交由本方全部棋子复用,比逐个调用 getMovePoints(pl) 更高效。

game.gameStart("RED")
const moves = game.generateMoves("RED") // MovePointList[]
const redPieces = game.currentLivePieceList.filter(p => p.side === "RED")
// moves[i] 对应 redPieces[i] 的走法列表

注意generateMoves 返回伪合法走法(不包含送将过滤)。需要严格合法走法请使用 generateLegalMoves

generateLegalMoves(side: PieceSide): Move[]

批量生成指定方所有存活棋子的合法走法列表(AI 搜索可直接使用)。与 generateMoves 的区别:对每个伪合法走法执行送将检测(checkGeneralInTrouble),只返回走子后己方将帅不会被攻击、也不会与敌方将帅对脸的走法。内部仅构建一次棋盘位表并复用,送将检测使用增量模拟(双槽位),无额外棋盘分配。

game.gameStart("RED")
const legalMoves = game.generateLegalMoves("RED") // Move[]
// legalMoves[i] = { from: Point, to: Point, captured: ChessOfPeice | null }
isLegalMove(side: PieceSide, from: Point, to: Point): boolean

判断指定方棋子从 from 走到 to 是否为合法走法(含送将过滤)。若 from 处没有该方棋子、或 to 不在该棋子可走范围内、或走子后己方将帅不安全,均返回 false

game.gameStart("RED")
const ok = game.isLegalMove("RED", new Point(8, 9), new Point(8, 8))
getPiecesOfSide(side: PieceSide): PieceList

获取指定方的存活棋子列表,顺序与 currentLivePieceList 中该方棋子的顺序一致(可直接与 generateMoves(side) 对齐棋子与走法)。

game.gameStart("RED")
const redPieces = game.getPiecesOfSide("RED") // PieceList
getCurrentPenCode(side: PieceSide): string

获取当前棋盘的 PEN 格式位置代码

exportTextBoard(options?: TextBoardOptions): string

导出当前棋盘文字布局。style 可为 chinese(默认)或 ascii;也可用独立函数 exportTextBoard(pieces, options)

game.gameStart("RED")
console.log(game.exportTextBoard({ style: "chinese" }))
console.log(game.exportTextBoard({ style: "ascii", showCoords: false }))
use(plugin: ChessPlugin) / unuse(name: string)

注册/卸载扩展插件。插件可携带 renderer(自定义棋盘/棋子绘制)、variant(变体规则钩子,如揭棋)、formatCell(文字导出单元格)。

game.use({
  name: "my-theme",
  renderer: {
    drawPiece(ctx, piece, style, layout) {
      // 自定义棋子外形,返回 true 跳过默认圆形
      return true
    }
  },
  variant: {
    name: "jieqi",
    getPieceDisplayName(piece) {
      return "暗" // 未翻开时显示
    },
    initBoard(game) { /* 自定义开局 */ },
    afterMove(ctx, game) { /* 翻子、揭示等 */ }
  }
})

GameInfo 也可直接传入 plugins / renderer / variant。更多见 example/framework-demo.js

getDrawLayout(): DrawLayout

返回当前棋盘绘制布局(格子尺寸、偏移、配色等),供自定义渲染器使用。

listenClick(e: MouseEvent): void

用于dom的点击事件,此方法棋子运动无动画,且为同步执行函数。

    const app = document.getElementById("canvas")
    const ctx = app.getContext("2d")
    const game = new ZhChess.default({
        ctx,
        gameWidth: 375,
        gameHeight: 375
    })
    // 绑定点击事件
    app.addEventListener("click", game.listenClick, false)
listenClickAsync(e: MouseEvent): void

用于dom的点击事件,此方法执行棋子运动有动画(根据 duration 来决定运动时长),且为异步执行函数。

    const app = document.getElementById("canvas")
    const ctx = app.getContext("2d")
    const game = new ZhChess.default({
        ctx,
        gameWidth: 375,
        gameHeight: 375,
        duration: 500 // 棋子运动时长 500ms
    })
    // 绑定点击事件
    app.addEventListener("click", game.listenClickAsync, false)
moveStr(str: string, side: PieceSide): UpdateResult

通过文字的形式根据 红黑方视角 来移动象棋。无移动动画,同步方法,返回移动结果。

10    
9     
8     
7   
6  
5 
4 兵    兵    兵    兵    兵
3    炮                炮
2                    
1 车 马 相 士 帅  士 相 马 车
  1  2  3  4  5  6  7  8  9 

已红方或黑方自己视角,靠近对方底线为进,靠近己方底线为退,横着走为平。例如:车1进1是指自己左边的车往前走一步。

game.moveStr("车1进1", "RED") // 红方 车1进1 返回 { flag:true } 或者 { flag:false, message:"xxx" } 
moveStrAsync(str: string, side: PieceSide, refreshCtx: boolean): Promise < UpdateResult >

跟moveStr方法作用一样,不过是异步的,有动画效果。 refreshCtx 表示 是否每次移动都更新画布。

on(e: K, fn: GameEventMap[K]): void

游戏监听事件(泛型签名,回调参数类型随事件名自动推导)

  • e为move时,fn函数的参数有 (peice: ChessOfPeice, cp: CheckPoint, enemyhasTrouble: boolean)

  • e为moveFail时,fn函数的参数有(peice: ChessOfPeice, p: Point, currentSideDanger: boolean, msg: string)

  • e为log时,fn函数的参数有(str: any)

  • e为over时,fn函数的参数有(winnerSide: PieceSide)

  • e为error时,fn函数的参数有(error: any)

removeEvent(e: K, fn: GameEventMap[K]): void

移除游戏的监听函数

setLivePieceList(pl: PieceList): void

设置当前存活棋子列表

setPenCodeList(penCode: string): void

根据pen代码格式来设置当前棋盘

建议参考 文章 博客 https://www.xqbase.com/protocol/cchess_fen.htm

update(pos: Point, mov: null | Point, side: PieceSide, post: boolean): UpdateResult

游戏根据坐标点 移动点来进行更新游戏运行数据。 post :是否由程序自己更新游戏状态。

返回的结果: 如果 post 为 false, 请检查返回的结果 move 是否为 true ,为true表示有返回回调函数cb,只有调用 cb() 游戏状态才会更新。如果 post 为 true,程序会自己更新游戏状态 只需要判断 是否更新成功即可!

updateAsync(pos: Point, mov: null | Point, side: PieceSide, refreshCtx: boolean, moveCallback?: UpdateMoveCallback): Promise < UpdateResult >

游戏根据坐标点 移动点来进行更新游戏运行数据。这是一个返回一个promise结果,也表示 这个方法是异步的。

const ctx = document.getElementById("canvas").getContext("2d")
const game = new ZhChess({})
game.gameStart("RED")
game.updateAsync(pos, mov, side, true, (posPeice, newPoint) => console.log(posPeice, newPoint)) // 每次运动都去绘画一次
game.updateAsync(pos, mov, side, false, (posPeice, newPoint) => game.draw(ctx)) // 每次运动 自己去调用游戏绘画