Skip to content

Commit

Permalink
add support for comments
Browse files Browse the repository at this point in the history
* add methods for creating/retrieving/removing comments
* parse bracket- and semicolon-style comments when loading PGNs
* output bracket-style comments when producing PGNs
  • Loading branch information
jtwires committed May 24, 2020
1 parent 20a890d commit 9c600cb
Show file tree
Hide file tree
Showing 3 changed files with 543 additions and 5 deletions.
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,99 @@ chess.validate_fen('4r3/8/X12XPk/1p6/pP2p1R1/P1B5/2P2K2/3r4 w - - 1 45')
// error: '1st field (piece positions) is invalid [invalid piece].' }
```

### .get_comment()

Retrieve the comment for the current position, if it exists.

```js
const chess = new Chess()

chess.load_pgn("1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 {giuoco piano} *")

chess.get_comment()
// -> "giuoco piano"
```

### .set_comment(comment)

Comment on the current position.

```js
const chess = new Chess()

chess.move("e4")
chess.set_comment("king's pawn opening")

chess.pgn()
// -> "1. e4 {king's pawn opening}"
```

### .delete_comment()

Delete and return the comment for the current position, if it exists.

```js
const chess = new Chess()

chess.load_pgn("1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 {giuoco piano} *")

chess.get_comment()
// -> "giuoco piano"

chess.delete_comment()
// -> "giuoco piano"

chess.get_comment()
// -> undefined
```

### .get_comments()

Retrieve comments for all positions.

```js
const chess = new Chess()

chess.load_pgn("1. e4 e5 {king's pawn opening} 2. Nf3 Nc6 3. Bc4 Bc5 {giuoco piano} *")

chess.get_comments()
// -> [
// {
// fen: "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2",
// comment: "king's pawn opening"
// },
// {
// fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
// comment: "giuoco piano"
// }
// ]
```

### .delete_comments()

Delete and return comments for all positions.

```js
const chess = new Chess()

chess.load_pgn("1. e4 e5 {king's pawn opening} 2. Nf3 Nc6 3. Bc4 Bc5 {giuoco piano} *")

chess.delete_comments()
// -> [
// {
// fen: "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2",
// comment: "king's pawn opening"
// },
// {
// fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
// comment: "giuoco piano"
// }
// ]

chess.get_comments()
// -> []
```

## Sites Using chess.js

- [chess.com](http://www.chess.com/)
Expand Down
283 changes: 283 additions & 0 deletions __tests__/chess.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,289 @@ describe("Load PGN", function() {

});

describe("Manipulate Comments", function() {
var is_empty = function (object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
return false;
}
}
return true;
};

it('no comments', function() {
var chess = new Chess();
expect(chess.get_comment())
.toBeUndefined();
expect(chess.get_comments())
.toEqual([]);
chess.move('e4');
expect(chess.get_comment())
.toBeUndefined();
expect(chess.get_comments())
.toEqual([]);
expect(chess.pgn())
.toEqual('1. e4');
});

it('comment for initial position', function() {
var chess = new Chess();
chess.set_comment('starting position');
expect(chess.get_comment())
.toEqual('starting position');
expect(chess.get_comments())
.toEqual([{fen: chess.fen(), comment: 'starting position'}]);
expect(chess.pgn())
.toEqual('{starting position}');
});

it('comment for first move', function() {
var chess = new Chess();
chess.move('e4');
var e4 = chess.fen();
chess.set_comment('good move');
expect(chess.get_comment())
.toEqual('good move');
expect(chess.get_comments())
.toEqual([{fen: e4, comment: 'good move'}]);
chess.move('e5');
expect(chess.get_comment())
.toBeUndefined();
expect(chess.get_comments())
.toEqual([{fen: e4, comment: 'good move'}]);
expect(chess.pgn())
.toEqual('1. e4 {good move} e5');
});

it('comment for last move', function() {
var chess = new Chess();
chess.move('e4');
chess.move('e6');
chess.set_comment('dubious move');
expect(chess.get_comment())
.toEqual('dubious move');
expect(chess.get_comments())
.toEqual([{fen: chess.fen(), comment: 'dubious move'}]);
expect(chess.pgn())
.toEqual('1. e4 e6 {dubious move}');
});

it('comment with brackets', function() {
var chess = new Chess();
chess.set_comment('{starting position}');
expect(chess.get_comment())
.toEqual('[starting position]');
});

it('comments for everything', function() {
var chess = new Chess();

var initial = chess.fen();
chess.set_comment('starting position');
expect(chess.get_comment())
.toEqual('starting position');
expect(chess.get_comments())
.toEqual([{fen: initial, comment: 'starting position'}]);
expect(chess.pgn())
.toEqual('{starting position}');

chess.move('e4');
var e4 = chess.fen();
chess.set_comment('good move');
expect(chess.get_comment())
.toEqual('good move');
expect(chess.get_comments())
.toEqual([
{fen: initial, comment: 'starting position'},
{fen: e4, comment: 'good move'}
]);
expect(chess.pgn())
.toEqual('{starting position} 1. e4 {good move}');

chess.move('e6');
var e6 = chess.fen();
chess.set_comment('dubious move');
expect(chess.get_comment())
.toEqual('dubious move');
expect(chess.get_comments())
.toEqual([
{fen: initial, comment: 'starting position'},
{fen: e4, comment: 'good move'},
{fen: e6, comment: 'dubious move'}
]);
expect(chess.pgn())
.toEqual('{starting position} 1. e4 {good move} e6 {dubious move}');
});

it('delete comments', function() {
var chess = new Chess();
expect(chess.delete_comment())
.toBeUndefined();
expect(chess.delete_comments())
.toEqual([]);
var initial = chess.fen();
chess.set_comment('starting position');
chess.move('e4');
var e4 = chess.fen();
chess.set_comment('good move');
chess.move('e6');
var e6 = chess.fen();
chess.set_comment('dubious move');
expect(chess.get_comments())
.toEqual([
{fen: initial, comment: 'starting position'},
{fen: e4, comment: 'good move'},
{fen: e6, comment: 'dubious move'}
]);
expect(chess.delete_comment())
.toEqual('dubious move');
expect(chess.pgn())
.toEqual('{starting position} 1. e4 {good move} e6');
expect(chess.delete_comment())
.toBeUndefined();
expect(chess.delete_comments())
.toEqual([
{fen: initial, comment: 'starting position'},
{fen: e4, comment: 'good move'}
]);
expect(chess.pgn())
.toEqual('1. e4 e6');
});

it('prune comments', function() {
var chess = new Chess();
chess.move('e4');
chess.set_comment('tactical');
chess.undo();
chess.move('d4');
chess.set_comment('positional');
expect(chess.get_comments())
.toEqual([{fen: chess.fen(), comment: 'positional'}]);
expect(chess.pgn())
.toEqual('1. d4 {positional}');
});

it('clear comments', function() {
var test = function(fn) {
var chess = new Chess();
chess.move('e4');
chess.set_comment('good move');
expect(chess.get_comments())
.toEqual([{fen: chess.fen(), comment: 'good move'}]);
fn(chess);
expect(chess.get_comments())
.toEqual([]);
};
test(function(chess) { chess.reset(); });
test(function(chess) { chess.clear(); });
test(function(chess) { chess.load(chess.fen()); });
test(function(chess) { chess.load_pgn('1. e4'); });
});

});

describe('Format Comments', function() {
it('wrap comments', function() {
var chess = new Chess();
chess.move('e4');
chess.set_comment('good move');
chess.move('e5');
chess.set_comment('classical response');
expect(chess.pgn())
.toEqual('1. e4 {good move} e5 {classical response}');
expect(chess.pgn({max_width: 16}))
.toEqual([
'1. e4 {good',
'move} e5',
'{classical',
'response}'
].join('\n'));
expect(chess.pgn({max_width: 2}))
.toEqual([
'1.',
'e4',
'{good',
'move}',
'e5',
'{classical',
'response}'
].join('\n'));
});
});

describe('Load Comments', function() {
var tests = [
{
name: 'bracket comments',
input: '1. e4 {good move} e5 {classical response}',
output: '1. e4 {good move} e5 {classical response}'
},
{
name: 'semicolon comments',
input: '1. e4 e5; romantic era\n 2. Nf3 Nc6; common continuation',
output: '1. e4 e5 {romantic era} 2. Nf3 Nc6 {common continuation}'
},
{
name: 'bracket and semicolon comments',
input: '1. e4 {good!} e5; standard response\n 2. Nf3 Nc6 {common}',
output: '1. e4 {good!} e5 {standard response} 2. Nf3 Nc6 {common}'
},
{
name: 'bracket comments with newlines',
input: '1. e4 {good\nmove} e5 {classical\nresponse}',
output: '1. e4 {good move} e5 {classical response}'
},
{
name: 'initial comment',
input: '{ great game }\n1. e4 e5',
output: '{ great game } 1. e4 e5'
},
{
name: 'empty bracket comment',
input: '1. e4 {}',
output: '1. e4 {}'
},
{
name: 'empty semicolon comment',
input: '1. e4;\ne5',
output: '1. e4 {} e5'
},
{
name: 'unicode comment',
input: '1. e4 {Δ, Й, ק ,م, ๗, あ, 叶, 葉, and 말}',
output: '1. e4 {Δ, Й, ק ,م, ๗, あ, 叶, 葉, and 말}'
},
{
name: 'semicolon in bracket comment',
input: '1. e4 { a classic; well-studied } e5',
output: '1. e4 { a classic; well-studied } e5'
},
{
name: 'bracket in semicolon comment',
input: '1. e4 e5 ; a classic {well-studied}',
output: '1. e4 e5 {a classic {well-studied}}'
},
{
name: 'markers in bracket comment',
input: '1. e4 e5 {($1) 1. e4 is good}',
output: '1. e4 e5 {($1) 1. e4 is good}'
},
{
name: 'markers in semicolon comment',
input: '1. e4 e5; ($1) 1. e4 is good',
output: '1. e4 e5 {($1) 1. e4 is good}'
}
];

tests.forEach(function(test) {
it(`load ${test.name}`, function() {
var chess = new Chess();
chess.load_pgn(test.input);
expect(chess.pgn())
.toEqual(test.output);
});
});
});

describe("Make Move", function() {

Expand Down
Loading

1 comment on commit 9c600cb

@victorocna
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just what I was looking for. Great stuff!

I wanted to apply it in my project, but the npm package does not include this commit.
Could you also make a release of this version so that the npm package is updated?

Please sign in to comment.