This library produces chess games databases from PGN files and provides some
predicates for manipulating these databases.
Once connected to a number of chess_db databases, information about the games
can be interrogated. (See chess_db_opening/2 for an example.)
Ideally we want to hook this pack to a web-based interface for playing the games as we select them.
Currently selected games can be saved to a PGN file and be displayed with any PGN displaying program.
?- pack_install( chess_db ).
The pack includes code to:
There are three example pgns in pack(chess_db/data/pgn)
and an example program, examples/short.pl
Packs:
By default, chess database dirs contain 3 sqlite databases:
game_info(Gid,Key,Val)
info about each gamegame_move(Gid,Num,Turn,Move)
game_orig(Gid,Orig)
; where Orig is the verbatim of the section in the PGN for that gameA number of databases can be connected at the same time. Operations are implicit to all open databases. Connecting is via chess_db_connect/2.
?- [pack('chess_db/examples/short.pl')]. ?- short. % creates a chess_db in subdirectory short/ from data/4ncl_short.pgn % and displays the game ids for games that start with [e4,e6] (French defence) ... ?- french. % creates a new pgn file from the base for the 2 games in short/ that start with e4,e6 % Using existing chess_db directory: /home/nicos/short % (short) Following games start with 1.e4,e6 gid(chdbs(<#4078544a8f7f0000>,<#4078544a8f7f0000>,<#4078544a8f7f0000>):1) gid(chdbs(<#4078544a8f7f0000>,<#4078544a8f7f0000>,<#4078544a8f7f0000>):31) % (short) Writing 1.e4,e6 starting games to: 'short/french.pgn' % Closing chess db: '/home/nicos/short' true. % open file short/french.pgn on program that can play the games eg linux> chessx short/french.pgn
The above example in detail.
Turn debugging of writing out the original games as they are added to the database.
?- debug( chess_db(original) ).
Create a database in fresh directory short/. The DB will be populated with games from pgn file chess_db/data/4ncl_short.pgn
The database is closed after it is populated.
?- chess_db( pgn('4ncl_short'), short, create(true) ). ..... 1. e4 e6 2. d4 d5 3. Nd2 Be7 4. Bd3 c5 5. dxc5 Nf6 6. Qe2 O-O 7. Ngf3 a5 8. O-O Na6 9. e5 Nd7 10. Nb3 Ndxc5 11. Bb5 Bd7 12. Bxd7 Qxd7 13. Nbd4 Ne4 14. Be3 f5 15. Qb5 Qxb5 16. Nxb5 Rfc8 17. c3 Nac5 18. Nfd4 Ra6 19. f3 Ng5 20. Rad1 Nf7 21. f4 Ne4 1/2-1/2 true.
Connect to the new database. The connections are managed internally.
?- chess_db_connect( short, profile(false) ). true.
Interrogate all connected databases for games starting with the sequence [e4,e6] (French defence).
?- findall( Gid, (chess_db_opening([e4,e6],Gid),write(Gid),nl), Gids ). chdbs(<sqlite>(0x276c930),<sqlite>(0x278f320),<sqlite>(0x2792450)):1 chdbs(<sqlite>(0x276c930),<sqlite>(0x278f320),<sqlite>(0x2792450)):31 Gids = [chdbs(<sqlite>(0x276c930), <sqlite>(0x278f320), <sqlite>(0x2792450)):1, chdbs(<sqlite>(0x276c930), <sqlite>(0x278f320), <sqlite>(0x2792450)):31].
Turn general library debugging on.
?- debug(chess_db).
Re-connecting is handled fine.
?- chess_db_connect( short, profile(false) ). % Handles already exist, for chess_db directory: '/home/nicos/short'
Create a new PGN file from the original scripts of the two French defence 2 games in their original script. Moves are matched to game_move/4 and are pulled from the game_orig/2 sub-database.
?- PgnF = 'short/french.pgn', chess_db_opening_pgn( [e4,e6], PgnF ).
View the two games in a PGN interface program such as:
?- shell( 'chessx short/french.pgn' ).
Listens to:
chess_db(move)
chess_db(original)
chess_db(true)
(on-by-default channel, turn off for silent operation)parsing of and saving to pgn files, and storing/retriving on/from chess_dbs
manage database connections
access db games and info
info interactions
miscellaneous
A Pgn term is of the form: pgn(Info,Moves,Res,Orig)
. Where, <br>
move(Num,Mv1,Mv2,Cms1,Cms2)
?- pgn( 4nclall1718.pgn, Pgn ). % in data/ ?- pgn( '18.03.10-Hampstead.pgn', Pgn ).
fixme:
Opts
Options can also be picked up from ~/.pl/chess_db.pl
(see options_append/3).
?- pgn( pgn('18.03-candidates'), Pgn ), chess_db( Pgn, chess_db('18.03-candidates'), [db(Which),create(true)] ). Pgn = ... Which = '.../swipl-7.7.18/pack/chess_db_data/dbs/18.03-candidates'. ?- chess_db( pgn('4ncl_short.pgn'), fourNCL, [dir('/tmp'),create(true),db(Db)] ). Db = '/tmp/fourNCL'.
?- chess_db_list( short ).
?- chess_db_current( CdbHs ), chess_db_max_id( CdbHs, Max ). Max = 31.
?- chess_db_connect( [dir('/usr/local/users/chess/chess_db/18.07-Biel'),profile(false),position(true)] ). ?- chess_db_game(Gid). Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):1 ; Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):2 ;
?- chess_db_ids_pgn( ..., ... ).
pack(chess_db_data/dbs)
). Second, via dir(Dir)
option.pl/chess_db_connect.pl
Opts
create(Create=false)
if true create dir and/or db files if they do not existdb(Db)
returns the absolute locations of the dbs successfully connected (a list iff more than one)dir(Dir)
parent directory of chess database (mutliple are allowed)handles(Handles)
returns the handles term of connected databases (a list if multiple were established)profile(Prof=Prof)
whether to mix, true, or ignore, false, profile based dir options (see options_append/4)
if no dir(Dir)
option is present in Opts, then Prof is ignored% connect with alias ?- Db = chess_db('18.03-candidates'), ( chess_db_connect(Db, db(AbsDb) ) -> true ; chess_db( pgn('18.03-candidates'), Db, create(true) ), chess_db_connect( Db, db(AbsDb) ) ). Db = chess_db('18.03-candidates'), AbsDb = ['/usr/local/users/nicos/local/git/lib/swipl-7.7.18/pack/chess_db_data/dbs/18.03-candidates']. ?- chess_db_disconnect( Db ). Db = '/usr/local/users/nicos/local/git/lib/swipl-7.7.18/pack/chess_db_data/dbs/18.03-candidates'. % connect with dir option in profile options... ?- shell( 'cat ~/.pl/chess_db_connect.pl' ). dir( '/usr/local/users/chess/chess_db' ). dir( '/usr/local/users/nicos/local/git/lib/swipl/pack/chess_db_data/dbs' ). true. ?- read_link( '/usr/local/users/nicos/local/git/lib/swipl', A, B ). A = 'swipl-7.7.18/', B = '/usr/local/users/nicos/local/git/lib/swipl-7.7.18/'. ?- chess_db_connect('18.03-candidates', db(Db) ). Db = ['/usr/local/users/nicos/local/git/lib/swipl-7.7.18/pack/chess_db_data/dbs/18.03-candidates'].
?- debug( chess_db(original) ). ?- chess_db( pack('chess_db/data/pgn/4ncl_short.pgn'), fourNCL.pgn, [dir('/tmp'),create(true)] ). ..... ?- chess_db_connect( '/tmp/fourNCL', profile(false) ). true. ?- chess_db_current( Handles ). Handles = chdbs(<#40f8e51c617f0000>, <#40f8e51c617f0000>, <#40f8e51c617f0000>). ?- chess_db_current( Handles, Db ). Handles = chdbs(<#40f8e51c617f0000>, <#40f8e51c617f0000>, <#40f8e51c617f0000>), Db = '/tmp/fourNCL'. ?- chess_db_current( CdbHs ), chess_db_max_id( CdbHs, Max ). CdbHs = chdbs(<#40f8e51c617f0000>, <#40f8e51c617f0000>, <#40f8e51c617f0000>), Max = 31.
?- chess_db_connect( [dir('/usr/local/users/chess/chess_db/18.07-Biel'),profile(false),position(true)] ). ?- chess_db_game(Gid), chess_db_game_info(Gid,'Result',Result). Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):1, Result = '1-0' ; Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):2, Result = '1-0' ; Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):3, Result = '1/2-1/2' ; Gid = chdbs(<#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>, <#40380f90857f0000>):4, Result = '0-1' ... ?- % give me all Sicilian defence results with opponent names ?- chess_db_opening( [e4,c5], Gid ), chess_db_game_info( Gid, 'Result', Result ), chess_db_game_info( Gid, 'White', White ), chess_db_game_info( Gid, 'Black', Black ), write( Result:White/Black ), nl, fail. 1-0:Svidler, Peter/Georgiadis, Nico 1/2-1/2:Carlsen, Magnus/Svidler, Peter 0-1:Georgiadis, Nico/Mamedyarov, Shakhriyar 1/2-1/2:Carlsen, Magnus/Vachier-Lagrave, Maxime 0-1:Georgiadis, Nico/Svidler, Peter 1-0:Carlsen, Magnus/Georgiadis, Nico (*):Carlsen, Magnus/Georgiadis, Nico
Match-terms atom (exact match to any info label) + terms (conjuction of the plused terms) Key(InfoMatch) where only Key keys are (no-cased) matches with InfoMatch ( -Key means do not match Key's case)
sub(Atom)
where Atom is matched with sub_atom/2sub(- Atom)
where Atom is matched with sub_atom/2 and no cased?- chess_db_connect( chess_db(gms) ). ?- chess_db_match( 'So, Wesley', count(Count) ). Count = 40 ?- chess_db_match( [-white(sub('So'))], Gid ), Gid = _:Gno, chess_db_id_info(Gid,'White',White), write( Gno:White ), nl, fail. ... 196:So,W ... 305:So, Wesley ?- chess_db_match( [-white(sub('So'))], count(Count) ). Count = 27. ?- chess_db_match( [-black(sub('So'))], count(Count) ). Count = 27. chess_db_match( [-black(sub('So'))], Gid ), Gid = _:Gno, chess_db_id_info(Gid,'White',White), write( Gno:White ), nl, fail. 6:Caruana, Fabiano 16:Topalov, Veselin ... ?- chess_db_match( [white(sub('So'))], count(Count) ). Count = 0. ?- chess_db_match( ['White'(sub('So'))], count(Count) ). Count = 27. ?- chess_db_match( sub('So,'), count(Count) ). ?- chess_db_match( sub('So,'), 'SoWesley' ).
?- chess_db_opening( [e4,e6], Gid ). % find a French defence game.
Db can be
?- debug( chess_db(original) ). ?- chess_db( pack('chess_db/data/pgn/4ncl_short.pgn'), fourNCL, [dir('/tmp'),create(true),db(Db)] ). ... Db = '/tmp/fourNCL'. ?- chess_db_connect( fourNCL, [dir('/tmp'),profile(false)] ). true. ?- chess_db_current( CdbHs ). CdbHs = chdbs(<#40e867375c7f0000>, <#40e867375c7f0000>, <#40e867375c7f0000>). ?- chess_db_current( CdbHs ), chess_db_disconnect( CdbHs ).
?- chess_db( pack('chess_db/examples/4ncl_short.pgn'), short, [create(true)] ). ?- chess_db_connect( short, profile(false) ). ?- PgnF = 'short/french.pgn', chess_db_opening_pgn( [e4,e6], PgnF ).
date(Y,M,D)
term.
?- chess_db_version( 0:3:0, date(2018,3,21) ). true.