brozeph / node-chess Goto Github PK
View Code? Open in Web Editor NEWA simple node.js library for parsing and validating chess board position with an algebraic move parser
Home Page: https://brozeph.github.io/node-chess
License: MIT License
A simple node.js library for parsing and validating chess board position with an algebraic move parser
Home Page: https://brozeph.github.io/node-chess
License: MIT License
To repro:
const gameClient = chess.create();
let gameMove = gameClient.move("e4");
gameMove.undo();
Throws this exception:
TypeError: Cannot read properties of undefined (reading 'piece')
This moveset, followed by Rg6
makes another black pawn appear in a6
. Seems similar to #1
g.move 'e4'
g.move 'e5'
g.move 'd3'
g.move 'Nc6'
g.move 'Nf3'
g.move 'Bb4'
g.move 'Nfd2'
g.move 'd6'
g.move 'a3'
g.move 'Bc5'
g.move 'Be2'
g.move 'Qf6'
g.move '0-0'
g.move 'Bxf2'
g.move 'Rxf2'
g.move 'Qe6'
g.move 'Nc4'
g.move 'Nd4'
g.move 'Bf1'
g.move 'Bd7'
g.move 'c3'
g.move 'Nb3'
g.move 'Ra2'
g.move 'Ba4'
g.move 'Qc2'
g.move 'Nh6'
g.move 'd4'
g.move 'Ng4'
g.move 'Rf3'
g.move 'b5'
g.move 'Nxe5'
g.move 'Nxc1'
g.move 'Qxc1'
g.move 'dxe5'
g.move 'Ra1'
g.move 'Rb8'
g.move 'h3'
g.move 'Rb6'
g.move 'hxg4'
g.move 'Qxg4'
g.move 'Nd2'
g.move 'a5'
g.move 'dxe5'
g.move 'Rc6'
g.move 'c4'
g.move 'h5'
g.move 'Rb1'
g.move 'Rhh6'
g.move 'Ra1'
g.move 'Rce6'
g.move 'Bd3'
g.move 'Rxe5'
g.move 'cxb5'
g.move 'Rg6'
TS7016: Could not find a declaration file for module chess.
~/coding/chess/server/node_modules/chess/src/main.js
implicitly has an any type.
There are types at
~/coding/chess/server/node_modules/chess/@types/chess/chess.d.ts
, but this result could not be resolved when respecting package.json exports. The chess library may need to update its package.json or typings
Editor : WebStorm 2024.1.2
I'm unsure if related to #43
I've tried this dumb game for trying the en passant:
game.move('e4')
game.move('d5')
game.move('e5')
game.move('f5')
game.move('exf6') // En passant
It seems exf6
is not in the list, but rather f6
is present. I need to import/export moves in PGN and f6
wouldn't be valid I believe. Since I'm not an expert in PGN format at all, maybe I'm lost in some ambiguity there? How should we address this kind of issue then?
Currently I can't load a game from a FEN string
Your latest git commit may have fixed this, but I ran my test against the npm version 0.1.7.
The following position does not detect a check with the knight.
g.move 'd4'
g.move 'd5'
g.move 'f4'
g.move 'e6'
g.move 'g3'
g.move 'Bd6'
g.move 'b4'
g.move 'Nc6'
g.move 'c3'
g.move 'Nf6'
g.move 'Nh3'
g.move 'Nxb4'
g.move 'cxb4'
g.move 'Bxb4'
g.move 'Nd2'
g.move '0-0'
g.move 'Bb2'
g.move 'b6'
g.move 'a3'
g.move 'Bd6'
g.move 'Rc1'
g.move 'Ng4'
g.move 'Rc6'
g.move 'Ne3'
g.move 'Qc2'
g.move 'Nxc2'
It would be nice to have the square of captured piece to avoid calculating variants like enPassant
const { move } = this.game.move(algebraic)
if (move.capturedPiece) {
console.log('Captured Piece', move.capturedPiece.square) // { file: 'd', rank: 5, piece: Pawn }
}
I understand this would be recursive adding a Square in a Piece type, but I'd like to find a way to make it simple.
What do you think?
UCI engines such as Stockfish use a special move syntax. See here: https://en.wikipedia.org/wiki/Universal_Chess_Interface#Design
This would be very nice, as you could directly use this in conjunction with an engine which would enable you to analyze and such
I was using node-chess as the backend for a small chess-focussed app that I'm building, and I noticed some strange behavior. If you play through the following moves, you'll notice that an additional black pawn appears spontaneously.
1. e4 e5 2. Nf3 Nc6 3. Bb5 Nf6 4. O-O Nxe4 5. d4 Nd6 6. Bxc6...
After the first half of the sixth move, a ninth black pawn appears at c5.
In file game.js on line 90 there should be a board parameter passed to the game object.
I encountered this bug a while back so hopefully my thinking is still accurate.
Steps to reproduce: underpromote any pawn or explicitly promote the pawn to a queen (i.e. append "Q" to notation)
Issue: Pawn is not promoted at all.
Suggested Fix:
// check for pawn promotion
if (notation.charAt(notation.length - 1).match(/[BNQR]/)) {
promo = notation.charAt(notation.length - 1);
notation = notation.slice(0, notation.length - 1); // <------- remove it from the notation
}
Basically, if I can recall correctly, the notation when not stripped of the promotion character will cause the function to recurse on line 267 and return immediately, and thus never hit the promotion code on lines 275 and onward. Taking away the promo character allows it to fall through to the promotion code.
It would be nice to have quick access to the array of captured pieces inside the Game instance.
I think it should be possible.
here:
"0-0-0":{"src":{"file":"e","rank":8,"piece":{"moveCount":0,"side":{"name":"black"},"type":"king","notation":"K"}},"dest":{"file":"c","rank":8,"piece":null}}
0-0-0 should really be O-O-O (if wikipedia is to be trusted). Same for O-O.
It does accept either notation 0-0 || O-O but should only ever return O-O.
I hope this makes sense.
Steps to reproduce:
Suggested fix below:
node-chess/lib/algebraicGameClient.js (lines 79 to 86)
// iterate through each starting squares valid moves
for (i = 0; i < validMoves.length; i++) {
p = validMoves[i].src.piece;
// iterate each potential move and build prefix and suffix for notation
for (n = 0; n < validMoves[i].squares.length; n++) {
prefix = '';
isPromotion = false; // <---- need to reset isPromotion
When attempting to parse the attached PGN file, the move "gxf3+" could not be parsed properly.
Parser reduces it to "gf3" instead of "f3", which is in the list of available moves. Likely because it is also a checking move (hence the +).
When I install with "npm install node-chess" or "yarn add chess"
CODE:
const chess = require("chess");
console.log(chess);
const gameClient = chess.create({ PGN: true });
gameClient.getStatus();
Output:
node:internal/modules/cjs/loader:361
throw err;
^
Error: Cannot find module '/home/darwin/Code/node/chess-whatsapp/node_modules/chess/src/main.js'. Please verify that the package.json has a valid "main" entry
at tryPackage (node:internal/modules/cjs/loader:353:19)
at Function.Module._findPath (node:internal/modules/cjs/loader:566:18)
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object.<anonymous> (/home/darwin/Code/node/chess-whatsapp/game.js:1:15)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32) {
code: 'MODULE_NOT_FOUND',
path: '/home/darwin/Code/node/chess-whatsapp/node_modules/chess/package.json',
requestPath: 'chess'
}
node: v16.16.0
npm: 8.11.0
yarn: 1.22.19
System:
No LSB modules are available.
Distributor ID: Kali
Description: Kali GNU/Linux Rolling
Release: 2022.2
Codename: kali-rolling
[email protected]: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
So please update dependencies from @babel/runtime-corejs2
I'm thinking of this as an enhancement since your objects are already extending an EventEmitter
What do you think of making this library more Events oriented? Would it add much more complexity to it?
This way we could sync a board using events only, and separate controls and feedback from the library.
If this use case seems legit to you, I might dig in.
I am building a nextjs project with the library but I can't get the typescript compilation to work.
this is my tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
I stored all the played moves and looped through to call undo()
.
The board and its pieces update to the previous locations but game.moveHistory
and getStatus().notatedMoves
do not.
I don't know if this was done on purpose or not, just thought i should report it .
First, thank you for putting your chess engine on npms. I built a chess engine a few years ago and was comparing my results with yours for the first 4 plys. I found that the results were slightly off. I wasn't sure which one was incorrect; so, I modified both to output the results in the same format. I then wrote another app to digest the values and determine the differences. Long story short, the issue was on the chess.js side. Your logic for pawn movements has a small bug. It's not accounting for potential double-jump if there is also a capture possible.
Try this: e2e4,a7a5,f1a6,b7b5. The last move presented is a very valid move which you can verify on another chess app or manually. However, chess.js does not realize this is a valid move. I believe the pawn logic below is getting confused because there is a capture scenario. Though the pawn can... capture, it doesn't have to. It is very legal to still double-jump.
We should definitely sync up sometime. I did a ton of optimization earlier for my chess engine. Feel free to reach out to me. I hope you are able to get this defect fixed soon. I can double check your results again afterwards.
Thanks,
Ronnie
[email protected]
//pawn logic that I found below
// check for double square first move
if (opt.piece.moveCount === 0 &&
opt.destSquares.length === 1 &&
opt.destSquares[0].piece === null) { // Fix for issue #1
sq = this.board.getNeighborSquare(
opt.destSquares[0],
opt.piece.side === piece.SideType.White ?
board.NeighborType.Above :
board.NeighborType.Below
);
if (!sq.piece) {
opt.destSquares.push(sq);
}
I created this declaration file for default autocomplete and intellisense in VSCode
The latest version on npm seems to be missing the dist folder.
resulting in
require("chess")
Error: Cannot find module 'chess'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:594:15)
at Function.Module._load (internal/modules/cjs/loader.js:520:25)
at Module.require (internal/modules/cjs/loader.js:650:17)
at require (internal/modules/cjs/helpers.js:20:18)
you can reproduce the issue here https://npm.runkit.com/chess
Didn't see anything in the docs/api section with regards to pawn promotion.
I moved a white pawn across the board to e7. Available moves at that point when it was my turn again was e8, but I would expect to see e8Q, e8N, e8R, etc.
I have created a fork and may consider implementing and testing this myself. We'll see if I have time.
Thoughts/concerns?
> const chess = require('chess');
undefined
> const gameClient = chess.create({ PGN: true });
undefined
> let m = gameClient.move("e4");
undefined
> m = gameClient.move("c5");
undefined
> m.undo()
undefined
> gameClient.move("e5");
{
move: {
algebraic: 'e5',
capturedPiece: null,
castle: false,
enPassant: false,
postSquare: Square { file: 'e', piece: [Pawn], rank: 5 },
prevSquare: Square { file: 'e', piece: null, rank: 4 } // Here's the bug. The prevSquare should be e7
},
undo: [Function (anonymous)]
}
@brozeph Is it possible to create move variations using this package...?
Line 88 in 93afab6
On Board.load(fen)
function it is represented as 'p' and 'P'; So, why is it represented as an empty string on the PawnPiece.notation
class field?
I'm rewriting this library in Dart, so I'm wondering if it has some relevance, or may I can replace it with P
.
TIA.
In the README examples, the module chess
is imported using require
. However, this module is an ES6 module and doesn't allow itself to be require
d (at least for me).
This is the error I get when trying to require the module:
var Chess = require('chess').Chess;
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\Joey\Programming\Node.js\node_modules\chess\src\main.js from C:\Users\Joey\Programming\Node.js\chess.js not supported.
Instead change the require of main.js in C:\Users\malvi\Programming\Node.js\ChessBot\chessjstest.cjs to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (C:\Users\Joey\Programming\Node.js\chessjs.js :3:13) {
code: 'ERR_REQUIRE_ESM'
}
Node.js v17.0.
And this is the code used to generate this error:
const Chess = require('chess').Chess;
Do the examples need to be updated, or am I doing something wrong?
This position is registered as a check, and it should be a checkmate. I am able to do Kf7
to move the king to a position capturable by the knight.
g.move 'e4'
g.move 'e5'
g.move 'Nc3'
g.move 'd6'
g.move 'Bc4'
g.move 'Be6'
g.move 'Bb3'
g.move 'Nf6'
g.move 'Nge2'
g.move 'Nh5'
g.move 'Bxe6'
g.move 'fxe6'
g.move 'd4'
g.move 'Be7'
g.move 'dxe5'
g.move 'dxe5'
g.move 'Qxd8'
g.move 'Bxd8'
g.move 'Be3'
g.move '0-0'
g.move '0-0-0'
g.move 'Nc6'
g.move 'Rhf1'
g.move 'Bh4'
g.move 'Nb5'
g.move 'Rac8'
g.move 'f3'
g.move 'a6'
g.move 'Nbc3'
g.move 'Nb4'
g.move 'Bc5'
g.move 'Nxa2'
g.move 'Nxa2'
g.move 'b6'
g.move 'Bxf8'
g.move 'Rxf8'
g.move 'Nb4'
g.move 'a5'
g.move 'Nc6'
g.move 'Ra8'
g.move 'Nxe5'
g.move 'c5'
g.move 'Rd6'
g.move 'Rc8'
g.move 'Rxb6'
g.move 'c4'
g.move 'f4'
g.move 'c3'
g.move 'Nxc3'
g.move 'Rxc3'
g.move 'Rb8'
g.move 'Kf7'
Similarly, in this case it should detect a check:
g.move 'e4'
g.move 'e5'
g.move 'd3'
g.move 'Bc5'
g.move 'Nf3'
g.move 'Qf6'
g.move 'Be3'
g.move 'Na6'
g.move 'd4'
g.move 'Bb6'
g.move 'dxe5'
g.move 'Qe6'
g.move 'Bd4'
g.move 'Nc5'
g.move 'Ng5'
g.move 'Qg6'
g.move 'Be3'
g.move 'Nxe4'
g.move 'Nxe4'
g.move 'Qxe4'
g.move 'Na3'
g.move 'Qxe5'
g.move 'Bc4'
g.move 'Qxb2'
g.move 'Nb5'
g.move 'Nf6'
g.move '0-0'
g.move 'd5'
g.move 'Bb3'
g.move 'c6'
g.move 'Nd6'
This is a feature request rather than an issue. I am using the moves history (game.moveHistory) to generate a moves string that is compatible with UCI based chess engines. Looks like this: a2a4 e7d6 ...
This works fine however a pawn promotions in this notation is: h7h8q (for a promotion to queen). The move history does not include details of the promotion or even the algebraic notated move which could use to reconstruct this.
Is tis info available elsewhere or any chance you can include it?
g2 to g4
e7 to e5
f1 to h3
f7 to f5
g1 to f3
f5 to g4
h3 to g4
g8 to f6
After this set of moves the white bishop always disappears
try { gameClient.move(start, to); } catch (e) { console.error('Chess Client: ',e.message); }
logs Chess Client: move is not defined
when start is g4 ( where the bishop is supposed to be) after that if another white piece is moves the bishop is removed from the board and the square has no piece. I'm using simple game client if that matters, and the issue is reproducible and happens every time.
This is a feature request
It would be nice to see a simple api to dump game state to json and restore it. Something like
let json = game.dump()
game.restore(json)
This could be used to save games in a database or create save/restore points.
When you create a new game and play: d4 a6 d5 e5 the board object returns an additional black pawn out of nowhere at e6.
Looks like this has to do with en passant and the 'real' black pawn at e5 being threatened by the white pawn on d5.
This is the only issue I have encountered so far. Great work and thanks for sharing!
I have been messing around with this for a project I am working on and I think it would be valuable to have a way to use this to detect the opening based on the match as it has been played.
Do you think this is something that would be possible? I don't think I see this capability by any other NPM package and it could be a nice feature to add to an already great tool.
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.