For searching/filtering pgn files of chess games.
Features
Games are stored in a collection of type pgn_filter::Games, and can be filtered using the search method. The search method takes a BoardFilter definition, which specifies the permitted number of pieces of each type, and returns a new collection containing just those games which match the definition.
For example, the following filter matches 5-4 rook-and-pawn endings, i.e. those with 5 white pawns, 4 black pawns and a rook on either side:
let filter = pgn_filter::Board::must_have()
.exactly(1, "R")
.exactly(1, "r")
.exactly(5, "P")
.exactly(4, "p");
Notice that the filter is constructed from a base filter obtained by calling "must_have". This base filter recognises positions with exactly one king of each colour, and no pieces or pawns. You build up the filter by adding a relation for each of the pieces you require. If you add a second filter for a given piece, the second filter will replace the first one.
The available filters are:
The following example shows how to construct a filter to identify 5-4 rook endings, and save the selected games to a file.
``` // Create a database and add some games let mut db = pgnfilter::Games::new(); db.addgames("examples/twic1356.pgn").unwrap();
// Display some information println!("Database has {} game(s)", db.iter().count());
// Create a position definition for 5-4 rook endings let filter = pgnfilter::Board::musthave() .exactly(1, "R") .exactly(1, "r") .exactly(5, "P") .exactly(4, "p");
// Extract those games which at some point reach let selected = db.search(&filter); selected.to_file("rook-endings.pgn").unwrap();
// Report and save result println!("Selected {} out of {} games", selected.iter().count(), db.iter().count()); ```
The Game#play_game method takes a closure/function, which is passed the current board and next move after each half move. The following example prints out the board position and information to create a trace of the game:
``` let fischer = pgnfilter::Games::fromfile("examples/fischer.pgn").unwrap();
println!("File has {} game(s)", fischer.games.len());
if fischer.games.len() > 0 { let game = &fischer.games[0];
game.play_game(|board, next_move| {
println!("{}", board.to_string());
match next_move {
Some(mv) => {
// next move in the game
println!(
"Move {}: {}{}",
board.fullmove_number,
if board.white_to_move { "" } else { "..." },
mv
);
println!("");
}
None => {
// game over, so display the result
println!("Result: {}", game.header.get("Result").unwrap());
}
};
});
} ```
Sample output:
``` File has 1 game(s) rnbqkbnr pppppppp ........ ........ ........ ........ PPPPPPPP RNBQKBNR
Move 1: e4
rnbqkbnr pppppppp ........ ........ ....P... ........ PPPP.PPP RNBQKBNR
Move 1: ...e5
rnbqkbnr pppp.ppp ........ ....p... ....P... ........ PPPP.PPP RNBQKBNR
Move 2: Nf3
rnbqkbnr pppp.ppp ........ ....p... ....P... .....N.. PPPP.PPP RNBQKB.R
Move 2: ...Nc6
r.bqkbnr pppp.ppp ..n..... ....p... ....P... .....N.. PPPP.PPP RNBQKB.R
Move 3: Bb5 ```