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 either a web-based interface, or (b) have an engive interface to exploit GUI playing programs, 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).
There are three example pgns in pack(chess_db/data/pgn) and an example program in pack(chess_db/examples/short.pl).
The best way to view the documentation is via the SWI-Prolog html documentation server:
?- doc_server(3004).
% Started server at http://localhost:3004/pldoc/
true.
?- use_module(library(chess_db)).
true.
?- www_open_url('http://localhost:3004/pldoc' )
% Which will open a browser pointing to the doc server. At this page search for the term: chess_db.
Static docs version is available at: https://stoics.org.uk/~nicos/sware/chess_db/doc/html/chess_db.html
Home page: https://stoics.org.uk/~nicos/sware/chess_db
The source code for the pack is available at github: https://github.com/nicos-angelopoulos/chess_db
Packs:
Latest test on SWI-Prolog 9.3.34 (2025/12/04).
The pack includes code to:
See below for details on each.
A number of chess 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 on- 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(info)).
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' ).
Find continuations for specific positions
?- chess_db_connect( short, profile(false) ). ?- chess_db_position( [e4,e6], Handles, Conts ). Handles = chdbs(<sqlite>(0xaaaae1482af0), <sqlite>(0xaaaae1487890), <sqlite>(0xaaaae148c740), <sqlite>(0xaaaae1491500)), Conts = '1-3-d3;31-3-d4' ; false
Please note that this is more generic than chess_db_opening/2 as it also finds transpositions.
Listens to:
chess_db(info)
trigger light reporting across the librarychess_db(stats)
reports stats of progress points as readable terms via pack(debug_call) debugoal stats.chess_db(game)
reports which game is assertedchess_db(pgn(info)) print names of players for read-in gamechess_db(pgn(moves)) report the pgn moves read-inchess_db(pgn(move)) report aspects of the move currently working onchess_db(original)chess_db(true)
(on-by-default channel, turn off for silent operation)Portable Game Notation is the standard for recording chess games. Each file, typically with a .pgn extension, may contain multiple games. In most cases a single PGN file will contain all the games a particular chess tournament.
For example the file pack(chess_db('data/pgn/4NCL-2017.pgn') records some of the games for the 2017, 4NCL league taking place in the UK.
This pack implements a parser for PGNs (pgn/2) that converts the games in the .pgn file to a Prolog terms list, where each game is
represented by a pgn(Info,Moves,Res,Orig) term (see pgn/2).
The following two queries are equivalent
?- pgn( pack(chess_db/data/pgn/'4NCL-2017.pgn'), Pgn ).
Pgn = [pgn(['Event'-'4NCL Division 1a',...]),...]
?- pgn( pgn('4NCL-2017.pgn'), Pgn ).
Pgn = [pgn(['Event'-'4NCL Division 1a',...]),...]
To disambiguate between multiple pieces that maybe able to be refered to by a game move, the pack implements a pinned piece logic. The non-interface predicate implementing this is: chess_dict_move_pin/3.
Disambiguation is needed in the following example. On a board with white rooks on b2 and b7 a move noded by Rb3, could refer to either of the rooks moving to this square. If both rooks are unconstrained the move should have be recordes as either R2b3 or R7b3. If the move is recorded as Rb3, it means that either of the two rooks is constrained by a pin: ie moving the rook would put the white king under attack. For instance, if the white king is at a1 and the black queen is on c3, then the rook on b2 is pinned and moving it to b3 whould be an illegal move. Thus, in this context Rb3 refers to moving the rook on b7 to b3.
By default, each chess_db database directory contains 4 DBs each holding a single table.
Currently there are 2 db backends supported: rocksDB (via pack(rocksdb)) and sqlite (via pack(prosqlite)).
In both database instances we store each table in separate database.
RocksDB tables. Each table/database is stored within its own directory.
Prosqlite tables. In the following, a + sign prefixes a key field:
game_info(+Gid,+Key,Val) info Key->Val pairs about each gamegame_move(+Gid,+Ply,Hmv,Move)
game_orig(+Gid,Orig); where Orig is the verbatim of the section in the PGN for that gamegame_posi(+Posi,GPPairs), where GPPairs is a ; seperated Move- pairs which includes a last section of
highest ELO games that include the position ((eg '1-2-e4;2-4-e3;Bests', where Bests is of the form N:NthELO,Gid,
(Posi is converted to atom as RocksDB interface does not support long integers).game_posi(Posi,Conts); where Posi is a unique position and Conts is the continuations string (Gid-Ply-Mov, eg: 1-3-d5).Positions are encoded as long integers.
A number of chess_dbs can be opened at the same time.
Databases are created and managed with packs library(db_facts) and library(prosqlite).
The pack also implements boards as dictionaries along with predicates that effect transitions to the board due to moves.
Positions on the board get keys 1-64, and there are keys for: whether various castlings and any en passant are still valid, whose move it is, ply move and half moves since last take.
See:
Integer representation of positions. These hold less information than a board dictionary as we want to make positions that look the same identical, irrespective of when was the last take or at which stage (ply counter) of the game the position arose.
To convert from a dictionary use: chess_dict_inpo/2.
Inpos are used as the first field of game_posi/2 database tables (see Databases section above).
parsing of and saving to pgn files, and storing/retriving on/from chess_dbs
manage database connections
access db games and info
dictionaries
info interactions
documentation
date(Y,M,D) term.
?- chess_db_version(V, D). V = 1:2:0, D = date(2026, 2, 8).
A Pgn term is of the form: pgn(Info,Moves,Res,Orig). Where, <br>
move(Num,Mv1,Mv2,Cms1,Cms2)
?- pgn( pgn('4NCL-2017'), Pgn ). % in data/pgn
Pgn = [pgn(['Event'-'4NCL Division 1a', 'Site'-'Telford, ENG', 'Date'-'2017.11.11', ....] ...]
?- pgn( '18.03.10-Hampstead.pgn', Pgn ).
fixme:
By default games are added to current chess database pointed to by ChessDb or Opts.
As of v0.2 user define "processors" can be defined via option goal(Goal).
If both ChessDb and OptDb are given, ChessDb overrides.
If argument ChessDb and/or option OptDb is a variable, then the full location of the Db used is returned.
If Create = false, PgnOrF points to a file/dir and both Db and OptDb are variables,
then the stem of the PgnOrF is used as the name of Db. It is created in an existing directory
pointed to by a current alias defined in file_search_path/2- or the current directory
if no such alias exists.
To distinguish between the two arity 2 versions, Opts in that case needs to be a list.
Games in the database get a incremental unique id starting at 1 for first game.
If ChessDB was not connected previously, it will after the call, and it will remain so except if close(true) is passed in options.
As of v0.4 we can process PGNs in otherways, than adding to a database.
This is via defining Goal (goal() option). The goal will be called as
?- coal( Goal, Pgn, Giter, Progr, Pos, Tos, CdbHs, Niter ).
Pgn = list of PGN terms
Giter= Giter (input)
Prg = option progress(Prg) (invariant)
Pos = option position(Pos) (invariant)
Tos = option position_type(Tos) (invariant)
CdbHs= db handles; will be unbound variable if =|Giter \== chess_db_games_add|=
Niter= next Giter (output)
Opts
bests_limit(Bim=10)
limit of how many best game identifiers to keep for each positioncheck_game_exists(Chk=true)
whether to perorm some check that a game to be added doesnot already exists in db-
set to false if your pgns do not include repeatscreate(Create=false)
how to behave if ChessDb exists (see chess_db_connect/2)close(Close=false)
whether to close the connection to ChessDB after adding the gamesdb(OptDb)
database location (see chess_db_connect/2)dir(Dir)
directory where database is located, (many allowed, see chess_db_connect/2)goal(Goal=chess_db_games_add)
Goal for processing read-in game terms. The default adds them to database.
As of v0.4 alternatives are possible. See comment below.goal_iter(Giter)
Goal iterator, can be left unbound when addiing games to DB- in which case itwill become the
game id, instantiated by the predicate to the last integer game id in the database we adding to.
When user specific Goal is given, this can be used to pass information to it.goal_return(Gret)
returns the final value of Giterhandles(CdbHs)
handles for the databases(s) opened (as per chess_db_connect/2).incr(Incr=false)
if true incrementally add a game at a time via a temporary file
(default is to read whole PGN into memory and then spit out the terms to the database)progress(IProg=1000)
report (via debug(chess_db(true))) at every IProg games have been processedmax_games(MxG=inf)
only process first MxG number of games- only enforced when Incr=trueposition(Pos=true)
if true, use position tableposition_depth_limit(Pdl=inf)
number of first moves for which positions are kept for each game.
Decimals of the form .5 are allowed, 10.5 means keep postions until 10th white move (no black 10th).
Slightly counterintuitively, Pdl=:=10 will save positions for moves 1-10 inclusive of black move 10
(ie one more position than when Pdl=:=10.5).position_type(Pos=kvx)
type of the position table representation
pack(rocksdb) is installed and sqlite otherwise- requires pack(prosqlite)).
Options can also be picked up from ~/.pl/chess_db.pl (see options_append/3).
By default the predicate updates generates 4 tables
See pack doc chess_db for details.
You can chose which tables to generate/update using table/1,2 option. If one is given, then all tables should be given explicitly.
?- 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_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.plOpts
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 ).
Fen null can be either '-' which is what FEN uses, or 0 (which is how chess_db represents non
?- chess_fen_square( '-', Sq ). Sq = 0. ?- chess_fen_square( A1, 1 ). A1 = a1.
When Turn is given as a variable then it is instantiated to the move turn (DictI.0) in the dictionary), if it is non var/1, then it is checked against turn in DictI- failing or throwing a ball if they do not match, depending on the value of AtErr.
AtErr should be either fail or error.
?- chess_dict_start_board(Start), chess_dict_move(e4,Start,0,Mid), chess_dict_move('Nc6',Mid,1,End).
Start = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:1, 35:0, 36:0, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:0, fmv:0, hmv:0},
Mid = board{0:1, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:0, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:0, fmv:0, hmv:0},
End = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:0, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:0, fmv:1, hmv:1}.
?- chess_dict_start_board(Start), chess_dict_move(e4,Start,One), chess_dict_move(h5,One,Two), chess_dict_move('Ke2',Two,Thr), chess_dict_move('Rh6',Thr,For).
Start = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:1, 35:0, 36:0, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:0, fmv:0, hmv:0},
One = board{0:1, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:0, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:35, fmv:0, hmv:0},
Two = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:0, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:7, 62:0, 63:0, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:62, fmv:1, hmv:0},
Thr = board{0:1, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:0, 34:6, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:7, 62:0, 63:0, 64:10, cbk:1, cbq:1, cwk:0, cwq:0, eps:0, fmv:1, hmv:1},
For = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:0, 34:6, 35:0, 36:1, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:7, 62:10, 63:0, 64:0, cbk:1, cbq:0, cwk:0, cwq:0, eps:0, fmv:2, hmv:2}.
?- chess_dict_start_board(Start), chess_dict_move(d4,Start,Turn1,One), chess_dict_move(e5,One,Turn2,Two).
Start = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:1, 27:0, 28:0, 29:0, 30:0, 31:7, 32:11, 33:6, 34:1, 35:0, 36:0, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:0, fmv:0, hmv:0},
Turn1 = 0,
One = board{0:1, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:0, 27:0, 28:1, 29:0, 30:0, 31:7, 32:11, 33:6, 34:1, 35:0, 36:0, 37:0, 38:0, 39:7, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:27, fmv:0, hmv:0},
Turn2 = 1,
Two = board{0:0, 1:4, 2:1, 3:0, 4:0, 5:0, 6:0, 7:7, 8:10, 9:2, 10:1, 11:0, 12:0, 13:0, 14:0, 15:7, 16:8, 17:3, 18:1, 19:0, 20:0, 21:0, 22:0, 23:7, 24:9, 25:5, 26:0, 27:0, 28:1, 29:0, 30:0, 31:7, 32:11, 33:6, 34:1, 35:0, 36:0, 37:7, 38:0, 39:0, 40:12, 41:3, 42:1, 43:0, 44:0, 45:0, 46:0, 47:7, 48:9, 49:2, 50:1, 51:0, 52:0, 53:0, 54:0, 55:7, 56:8, 57:4, 58:1, 59:0, 60:0, 61:0, 62:0, 63:7, 64:10, cbk:1, cbq:1, cwk:1, cwq:1, eps:38, fmv:1, hmv:0}.
Each Limo = limo(Ply,Hmv,Mv,Inpo), where Ply, Hmv and Mv are of the current position where Inpo
is the the numerical representation of the move's parent position.
?- pgn( pgn('4ncl_short'), [PgnGame1|_] ), chess_pgn_limos( PgnGame1, Inpos ).
PgnGame1 = pgn(['Event'-'4NCL Division 1a', 'Site'-'Telford, ENG', 'Date'-'2017.11.11', 'Round'-'1.11', 'White'-'Sadler, Matthew D', 'Black'-'Wheeler, Darren P', 'Result'-'1-0', ... - ...|...], [move(1, e4, e6, '', ''), move(2, d3, d5, '', ''), move(3, 'Nd2', 'Nf6', '', ''), move(4, e5, 'Nfd7', '', ''), move(5, f4, b6, '', ''), move(6, g3, c5, '', ''), move(7, 'Bg2', 'Bb7', '', ''), move(..., ..., ..., ..., ...)|...], '1-0', [[91, 69, 118, 101, 110, 116, 32|...], [91, 83, 105, 116, 101, 32|...], [91, 68, 97, 116, 101|...], [91, 82, 111, 117|...], [91, 87, 104|...], [91, 66|...], [91|...], [...|...]|...]),
Inpos = [limo(0, 0, e4, 100700000000010408070000000001020907000000000103120700000000010611070000000001050907000000000103080700000000010210070000000001040030), limo(1, 0, e6, 100700000000010408070000000001020907000000000103120700000100000611070000000001050907000000000103080700000000010210070000000001003731), limo(2, 0, d3, 100700000000010408070000000001020907000000000103120007000100000611070000000001050907000000000103080700000000010210070000000001040030), limo(3, 0, d5, 100700000000010408070000000001020907000000000103120007000100000611070000000100050907000000000103080700000000010210070000000001040031), limo(4, 0, 'Nd2', 100700000000010408070000000001020907000000000103120007000100000611000007000100050907000000000103080700000000010210070000000001003230), limo(5, 1, 'Nf6', 100700000000010408070000000001020907000000000103120007000100000611000007000102050907000000000103080700000000010010070000000001040031), limo(6, 2, e5, 100700000000010400070000000001020907080000000103120007000100000611000007000102050907000000000103080700000000010010070000000001040030), limo(7, 0, 'Nfd7', 100700000000010400070000000001020907080000000103120007010000000611000007000102050907000000000103080700000000010010070000000001040031), limo(..., ..., ..., ...)|...].
Opts
?- chess_dict_pos_algebraic(1, Alg). ?- between( 1, 64, I ), chess_dict_pos_algebraic( I, Alg ), write( I:Alg ), nl, fail. 1:a1 2:a2 3:a3 .... ?- chess_dict_pos_algebraic(E4, e4).
Please notice that this a better way to look for position than chess_db_opening/2 as it also find transpositions.
Following from the example in the module docs, pack(chess_db):
?- chess_db_connect( short, db(Short) ), assert(short_db_handle(Short)). Short = '/home/nicos/pl/packs/private/chess_db/short'.
The following two queries are equivalent.
?- chess_db_position( [e4,e6], Handles, Conts ).
Handles = chdbs(<sqlite>(0xaaaae1482af0), <sqlite>(0xaaaae1487890), <sqlite>(0xaaaae148c740), <sqlite>(0xaaaae1491500)),
Conts = '1-3-d3;31-3-d4' ;
false
?- chess_dict_start_board(Board0),chess_dict_move(e4,Board0,Board1),chess_dict_move(e6,Board1,Board2),chess_dict_inpo(Board2,Inpo),
chess_db_position( Inpo, Handles, Conts ).
...
:2},
Inpo = ...,
Handles = chdbs(<sqlite>(0xaaaae1482af0), <sqlite>(0xaaaae1487890), <sqlite>(0xaaaae148c740), <sqlite>(0xaaaae1491500)),
Conts = '1-3-d3;31-3-d4' ;
false.
The following predicates are exported, but not or incorrectly documented.