jchesslib is a chess library for Java. It supports:
- move generation
- move validation
- managing game (trees)
- PGN import and export
- HTML export
- Polyglot and PolyglotExt opening books
Install via Maven.
Add this dependency to pom.xml
:
<dependency>
<groupId>io.github.asdfjkl</groupId>
<artifactId>jchesslib</artifactId>
<version>1.2</version>
</dependency>
Or download the jar here.
Javadoc for jchesslib is here
Scholar's mate:
Board board = new Board(true);
Move m1 = new Move("e2e4");
Move m2 = new Move("e7e5");
Move m3 = new Move("d1h5");
Move m4 = new Move("b8c6");
Move m5 = new Move("f1c4");
Move m6 = new Move("g8f6");
Move m7 = new Move("h5f7");
board.apply(m1);
board.apply(m2);
board.apply(m3);
board.apply(m4);
board.apply(m5);
board.apply(m6);
board.apply(m7);
System.out.println(board.isCheckmate());
>> true
Some functions change the board state so that undo is not possible anymore.
Always call isUndoAvailable
before undoing a move:
Board board = new Board(true);
Move m1 = new Move("e2e4");
board.apply(m1);
if(isUndoAvailable()){
board.undo();
}
Board board = new Board(true);
System.out.println(board);
>>> rnbqkbnr
>>> pppppppp
>>> ........
>>> ........
>>> ........
>>> ........
>>> PPPPPPPP
>>> RNBQKBNR
Board board = new Board("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
Move m = new Move("e7e5");
board.apply(m);
System.out.println(board.fen());
>>> rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 2
Board board = new Board(true);
board.isStalemate();
board.isCheckmate();
board.isInsufficientMaterial();
Game game = new Game();
Board board = new Board(true);
game.getRootNode().setBoard(board);
game.isThreefoldRepetition();
Scan a PGN and get file offsets of all games
String millbase = "/home/user/millionbase-2.22.pgn";
PgnReader reader = new PgnReader();
if(reader.isIsoLatin1(millbase)) {
reader.setEncodingIsoLatin1();
}
ArrayList<Long> offsets = reader.scanPgn(millbase);
Then seek to an offset and read the game
try {
raf = new OptimizedRandomAccessFile(millbase, "r");
offset = offsets.get(10);
raf.seek(offset);
Game g = reader.readGame(raf);
} catch (IOException e) {
e.printStackTrace();
}
Scan a PGN and get header information and file offsets of all games
String millbase = "/home/user/millionbase-2.22.pgn";
PgnReader reader = new PgnReader();
if(reader.isIsoLatin1(millbase)) {
reader.setEncodingIsoLatin1();
}
ArrayList<PgnItem> entries = reader.scanPgnGetSTR(millbase);
PgnItem entry = entries.get(10):
System.out.println("White: "+entry.getWhite());
Then seek to an offset and read the game
try {
raf = new OptimizedRandomAccessFile(millbase, "r");
PgnItem entry = entries.get(10):
offset = entry.getOffset();
raf.seek(offset);
Game g = reader.readGame(raf);
} catch (IOException e) {
e.printStackTrace();
}
Read the first game in a PGN
String filename = "/home/user/game.pgn";
OptimizedRandomAccessFile raf = null;
PgnReader reader = new PgnReader();
if(reader.isIsoLatin1(filename)) {
reader.setEncodingIsoLatin1();
}
try {
raf = new OptimizedRandomAccessFile(kingbase, "r");
Game g = reader.readGame(raf);
} catch (IOException e) {
e.printStackTrace();
}
Reading a PGN from a string:
String s = "[Event \"Berlin\"]\n" +
"[Site \"Berlin GER\"]\n" +
"[Date \"1852.??.??\"]\n" +
"[EventDate \"?\"]\n" +
"[Round \"?\"]\n" +
"[Result \"1-0\"]\n" +
"[White \"Adolf Anderssen\"]\n" +
"[Black \"Jean Dufresne\"]\n" +
"[ECO \"C52\"]\n" +
"[WhiteElo \"?\"]\n" +
"[BlackElo \"?\"]\n" +
"[PlyCount \"47\"]\n" +
"\n" +
"1.e4 e5 2.Nf3 Nc6 3.Bc4 Bc5 4.b4 Bxb4 5.c3 Ba5 6.d4 exd4 7.O-O\n" +
"d3 8.Qb3 Qf6 9.e5 Qg6 10.Re1 Nge7 11.Ba3 b5 12.Qxb5 Rb8 13.Qa4\n" +
"Bb6 14.Nbd2 Bb7 15.Ne4 Qf5 16.Bxd3 Qh5 17.Nf6+ gxf6 18.exf6\n" +
"Rg8 19.Rad1 Qxf3 20.Rxe7+ Nxe7 21.Qxd7+ Kxd7 22.Bf5+ Ke8\n" +
"23.Bd7+ Kf8 24.Bxe7# 1-0";
PgnReader reader = new PgnReader();
PgnPrinter printer = new PgnPrinter();
Game g = reader.readGame(s);
Writing a PGN to a file or to a String:
Game g = new Game();
g.setHeader("Event", "Training");
g.setHeader("Site", "Paris");
g.setHeader("Date", "1859.??.??");
g.setHeader("Round", "1");
g.setHeader("White", "Morphy");
g.setHeader("Black", "NN");
g.setHeader("Result", "1-0");
g.setHeader("ECO", "C56");
Board rootBoard = new Board(true);
g.getRootNode().setBoard(rootBoard);
g.applyMove(new Move("e2e4"));
g.applyMove(new Move("e7e5"));
g.applyMove(new Move("g1f3"));
g.applyMove(new Move("b8c6"));
g.applyMove(new Move("f1c4"));
g.applyMove(new Move("g8f6"));
g.applyMove(new Move("d2d4"));
g.applyMove(new Move("e5d4"));
g.applyMove(new Move("e1g1"));
g.applyMove(new Move("f6e4"));
PgnPrinter printer = new PgnPrinter();
System.out.println(printer.printGame(g));
printer.writeGame(g, "temp.pgn");
HtmlPrinter printer = new HtmlPrinter();
System.out.println(printer.printGame(g));
>>><b><span id="n1"><a href="#1">1. e4</a></span>
>>></b><b><span id="n2"><a href="#2">e5</a></span>
>>></b><b><span id="n3"><a href="#3">2. Nf3</a></span>
>>></b><b><span id="n4"><a href="#4">Nc6</a></span>
>>></b><b><span id="n5"><a href="#5">3. Bc4</a></span>
>>></b><b><span id="n6"><a href="#6">Nf6</a></span>
>>></b><b><span id="n7"><a href="#7">4. d4</a></span>
>>></b><b><span id="n8"><a href="#8">exd4</a></span>
>>></b><b><span id="n9"><a href="#9">5. O-O</a></span>
>>></b><b><span id="n10"><a href="#10">Nxe4</a></span> </b>*
Reading a Polyglot book. PolyglotExt books work similar.
Polyglot pg = new Polyglot();
File file = null;
URL book = getClass().getClassLoader().getResource("books/book.bin");
if(book != null) {
file = new File(book.getFile());
pg1.loadBook(file);
}
try {
PolyglotEntry e = pg.getEntryFromOffset(0x62c20);
System.out.println(e.uci);
System.out.println(e.weight);
System.out.println(e.learn);
Board b = new Board("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
long key = b.getZobrist();
ArrayList<String> entries = pg.findMoves(key);
for(String uci : entries) {
System.out.println(uci);
}
entries = pg.findMoves(0x2d3888dac361814aL);
for(String uci : entries) {
System.out.println(uci);
}
entries = pg.findMoves(0x823c9b50fd114196L);
for(String uci : entries) {
System.out.println(uci);
}
} catch(IllegalArgumentException e) {
e.printStackTrace();
}
jchesslib is not optimized for fast move generation, especially legal (not pseudo-legal) move generation is slow. It has reasonable performance to process large PGN files. Some benchmarks:
Library | Perft 6 (ms) |
---|---|
Stockfish 14.1 | 0.572 |
jchesslib | 18 |
python-chess (cpython) | 238 |
Processing a large (1.5 GB) PGN File:
jchesslib | seconds |
---|---|
scan PGN for game offsets | 4 |
read all games | 171 |
read all games and search for a position | 248 |
read all games and write to another file | 518 |
As a comparison:
Fritz 8 | seconds |
---|---|
read all games and search for a position | 332 |
jchesslib is licensed under MIT. See LICENSE for the full text.