chess_db.pl -- PGN and chess game databases.

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.

Installation:

?- 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

Dependencies

Packs:

Basics

The pack includes code to:

parse
PGN's: pgn/2
store
the parsed terms into prosqlite databases: chess_db/2
interact
with the resulting databases: chess_db_game_info/3, chess_db_opening_pgn/2
dictionary
representation of chess games: chess_dict_start_board/1, chess_dict_move/3
integer
representation of game positions: chess_dict_inpo/2
logic
for identifying pinned pieces (used in move disambiguation)

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.

Example

?- [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

Mini tutorial

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(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.

Debug terms

Listens to:

chess_db(info)
trigger light reporting across the library
chess_db(move)
chess_db(moves)
chess_db(original)
chess_db(true)
(on-by-default channel, turn off for silent operation)

PGNs

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.

Databases

By default, each chess_db database directory contains 4 SQLite DBs each holding a single table. In the following, a + sign prefixes a key field:

game_info.sqlite
contains game_info(+Gid,+Key,Val) info Key->Val pairs about each game
game_move.sqlite
table is, game_move(+Gid,+Ply,Hmv,Move)
game_orig.sqlite
table is game_orig(+Gid,Orig); where Orig is the verbatim of the section in the PGN for that game
game_posi.sqlite
table is game_posi(Posi,GPPairs), where GPPairs is a ; seperated Gid-Ply-Move pairs stored as text (eg '1-2-e4;2-4-e3'), Posi is a long integer stored as a string
game_posi.sqlite
table is 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).

Dictionaries

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:

Inpos

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).

Pack predicates

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

Pack info

author
- nicos angelopoulos
version
- 0.1 2018/3/18
- 0.2 2018/3/20
- 0.3 2018/9/14
- 1.0 2021/6/18
See also
- http://stoics.org.uk/~nicos/sware/chess_db
- https://github.com/nicos-angelopoulos/chess_db
- [pack('chess_db/examples/short.pl')]
- pack(prosqlite), pack(db_facts), pack(stoics_lib), pack(options)
To be done
- discard illegal moves when considering possible ones
 pgn(+PgnF, -Pgn)
pgn(+PgnF, +Pgn)
pgn(+PgnIn, -PgnOut)
Reads-from and writes-to between PGN files and Pgn term representations.
It can also be used to ensure second argument is a Pgn term when either a file or Pgn term is given as ground first argument.

A Pgn term is of the form: pgn(Info,Moves,Res,Orig). Where, <br>

?- 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:

  1. if we have result from both Info and end of moves, make sure they are the same <br>
  2. if the info missing and the result from end of moves exists, then add it to info
author
- nicos angelopoulos
version
- 0.1 2018/03/14
- 0.2 2018/08/01, changed from move(Num,Mvs,Cms), add $ starting NAGs, allow opening comment on game with moves
To be done
- complement the 2 ways of getting the result.
- currently valuation marks and variations are thrown away
 chess_db(+PgnOrF)
 chess_db(+PgnOrF, +ChessDb)
chess_db(+PgnOrF, +Opts)
 chess_db(+PgnOrF, +ChessDb, +Opts)
Add games from a PGN file or a PGN term, PgnOrF, to the chess database pointed to by Db and/or Opts. If Db is given both as argument and option, the argument overrides. If argument Db or option Db is a variable, then the full location of the Db used is returned. To distinguish between the two arity 2 versions, Opts in that case need be a list.

Opts

create(Create=false)
how to behave if ChessDb exists (see chess_db_connect/2)
db(Db)
database location (see chess_db_connect/2)
dir(Dir)
directory where database is located, (many allowed, see chess_db_connect/2)
position(Pos=true)
if true, use position table

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'.
author
- nicos angelopoulos
version
- 0.1 2018/3/14
- 0.2 2018/8/17
See also
- options_append/3
 chess_db_list(+Dir)
A simple, on-screen dump of all data in a chess games database.
Likely to be only useful for debugging.
?- chess_db_list( short ).
author
- nicos angelopoulos
version
- 0.1 2018/3/14
 chess_db_max_id(+HandleST, -Max)
For a handle or term of handles (in which case the first argument is used), return the Max id of the first column of the game_info on the Handle.
?- chess_db_current( CdbHs ), chess_db_max_id( CdbHs, Max ).
Max = 31.
author
- nicos angelopoulos
version
- 0.1 2018/3/15
 chess_db_game(-GameID)
Return all the unique game ids, constructed as Handles:Gno.
Handles are the handles to access an open chess id and Gno is the
unique game id for a game in that database.
?- 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 ;
author
- nicos angelopoulos
version
- 0.1 2018/8/15
 chess_db_ids_pgn(+GidS, +PgnF)
Save a number of games corresponding to list of, or single, id structures from the current dbs to a pgn file (PgnF).
?- chess_db_ids_pgn( ..., ... ).
author
- nicos angelopoulos
version
- 0.1 2018/8/19
 chess_db_connect(+DbS)
 chess_db_connect(+DbS, +Opts)
Connect to a number of chess_dbs (DbS) and provide make their db handles available
to a number of predicates that access the information: eg chess_db_opening/3.
The library provides two conveniencies for locating chess dbs. First, via aliases:
chess_db (by default expands to pack(chess_db_data/dbs)). Second, via dir(Dir) option.
In this case Dbs are looked for relative to all Dir locations provided.
Note that commonly used database directories can be defined long term in ~/.pl/chess_db_connect.pl
(see options_append/4).

Opts

create(Create=false)
if true create dir and/or db files if they do not exist if true create ChessDb if doesn't exist (and use current if it does) if false only proceed if ChessDb exists if new only proceed if ChessDb does not exist (call creates it) if fresh overwrites if a current exists
db(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'].
author
- nicos angelopoulos
version
- 0.1 2018/3/15
- 0.2 2018/8/17, added aliases, better locator (and order), fixes, docs/examples
See also
- chess_db_opening/3
 chess_db_current(-CdbHs)
 chess_db_current(-CdbHs, -Db)
Returns the handles structure and Db location, CdbHs, for each open database.<br>
?- 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.
author
- nicos angelopoulos
version
- 0.1 2018/3/15
- 0.2 2018/8/18, simplified to just an interface to chess_db_handles/2
See also
- chess_db_connect/1
 chess_db_game_info(+Gid, -Key, -Value)
Get Key and Value for info db corresponding to the game id GID.
Game id is of the form DBHandles:GameNo.
?- 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
author
- nicos angelopoulos
version
- 0.1 2018/8/15
 chess_db_match(+Match, ?Games)
Select a number of games from the open databases according to their info labels. If Games is a variable each matching Gid is returned on backtracking. Else, is taken to be a pgn file (via chess_db_connect/2), to which the originals from all matching games are dumped. Match is a list of the following type of terms. The list items are considered as a disjunction.

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)

?- 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' ).
author
- nicos angelopoulos
version
- 0.1 2018/8/19
 chess_db_opening(+Moves, ?GameS)
GameS is either a variable or a PGN filename. The predicate identifies all games starting with sequence of Moves. It either returns all game ids one at a time or saves them all the original PGN fragments for all matching ids to the output file. Moves is a list of atoms in standard chess notation.
GidStr is of the form GdbHs:Gid. Games are taken from data based identified by chess_db_current/1.
?- chess_db_opening( [e4,e6], Gid ). % find a French defence game.
author
- nicos angelopoulos
version
- 0.1 2018/3/15
 chess_db_disconnect
 chess_db_disconnect(?DbS)
Disconnect from a number of chess dbs. Can disconnect from all Dbs either one at a time when -Db using backtracking, or in a single deterministic call with the /0 version.

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 ).
author
- nicos angelopoulos
version
- 0.1 2018/3/18
 chess_db_opening_pgn(+Moves, +PgnF)
Dump all the original PGN fragments that correspond to Moves opening, to file PgnF.
?- 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 ).
author
- nicos angelopoulos
version
- 0.1 2018/3/17
See also
- pack('examples/short.pl')
 chess_fen_square(?Fen, ?Square)
As chess_dict_pos_algebraic/2 but allows null location when

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.
author
- nicos angelopoulos
version
- 0:1 2020/3/26
 chess_dict_move(+Move, +DictI, ?Turn, -DictO)
 chess_dict_move(+Move, +DictI, -DictO)
Enact Move and Turn in dictionary board DictI into new chess position DictO.

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- throwing a ball if they do not match.

?- 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}.
author
- nicos angelopoulos
version
- 0:1 2020/03/27
 chess_pgn_limos(+Pgn, -Limos)
Limos are all the long integer positions structures, generated by a (single) PGN game term.

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(..., ..., ..., ...)|...].
author
- nicos angelopoulos
version
- 0:1 2020/3/27
 chess_annotate_freq(+PgnF, +CheDb, +Opts)
Annotate the games in PGN with frequency statistics from CheDb.

Opts

postfix(Psf=annot)
postfix for the new file
author
- nicos angelopoulos
version
- 0:1 2020/03/29
To be done
- allow multiple Dbs
- break freqs as per result (reserve one for unknowns) ?
 chess_dict_pos_algebraic(+Pos, -Alg)
chess_dict_pos_algebraic(-Pos, +Alg)
?- 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).
 chess_db_position(+PosOrMoves, -DbHandles, -Conts)
For each open chess_db connection DbHandles, and for a Pos(ition) or list of Moves, returns the Continuations in this chess database.

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.
author
- nicos angelopoulos
version
- 0:1 2021/6/18
 chess_db_version(-Version, -Date)
The current version. Version is a Mj:Mn:Fx term, and date is a date(Y,M,D) term.
?- chess_db_version( 1:0:0, date(2021,6,18) ).
true.

Undocumented predicates

The following predicates are exported, but not or incorrectly documented.

 chess_dict_piece(Arg1, Arg2, Arg3)
 chess_dict_inpo(Arg1, Arg2)
 chess_dict_start_board(Arg1)