Converting Lucas Chess personal opening guides to PGN

Most chess software tools generally allow for importing/exporting between any of their own formats for chess games or positions or whatever and PGN. Lucas Chess seems to be, for the most part, no exception; however, while it allows for importing from PGN to its Personal Opening Guide structure (.pgo files), it oddly doesn’t have an option to export in the other direction. I’m not a Lucas Chess user myself, but I stumbled upon that fact in a forum post from someone wanting to export their .pgo file to PGN for use in some other chess software, and asking if someone could program a converter for them.

That piqued my curiosity, so here I’ll lay out a quick way to get from a .pgo file to a standard .pgn file that can be imported to other programs, using nothing but an R script and David Barnes’ free pgn-extract utility.

It turns out that each .pgo file is just a SQLite database containing a single table called GUIDE. Some of the columns of the GUIDE table look like this:


XPV                 PV      POS
---                 --      ---
DT                  c2c4    1
DTn^                e7e5    2
DTn^;L              b1c3    3
DTn^;Lxg            g8f6    4
DTn^;Lxg@O          g1f3    5
DTn^;Lxg@Osd        b8c6    6
DTn^;Lxg@OsdFN      e2e3    7
DTn^;Lxg@OsdFNwS    f8b4    8

As can be seen, the XPV column contains strings that encode sequences of moves (in some encoding scheme that we don’t need to worry about), and the corresponding PV entry gives the final move of the sequence in long algebraic notation. The XPV for the eighth entry above encodes the sequence (in standard algebraic notation) 1.c4 e5 2.Nc3 Nf6 3.Nf3 Nc6 4.e3 Bb4. Ultimately, we need to get all of the move sequences contained in the GUIDE table into this standard algebraic notation that .pgn files use. We do this in two steps: first an R script that constructs the sequences in long algebraic notation, and then using pgn-extract to convert that to a proper .pgn file that can be imported and used freely.

The following R script converts a file called lucas_guide.pgo into a file called intermediate_output.txt that consists of all move sequences from the GUIDE table, presented in long algebraic notation.

## Path to your Lucas Chess .pgo file
yourPGOfile <- "lucas_guide.pgo"
## Path to the output file for this script, which will then feed to
## pgn-extract
outputFilePath <- "intermediate_output.txt"


library(RSQLite)


## Use the RSQLite package to read the .pgo chess content into a data
## frame
con <- dbConnect(drv=RSQLite::SQLite(), dbname=yourPGOfile)
openingGuide <- dbGetQuery(conn=con,
                           statement="SELECT * FROM 'GUIDE'")
dbDisconnect(con)

## We're only going to work with a couple of columns
openingGuide <- openingGuide[,c("XPV","PV")]

## Determine which moves in the guide are terminal (end of a line)
allXPV <- openingGuide$XPV

isEndOfLine <- function(x) {
  1-max(sapply(allXPV,
               FUN=function(y){grepl(x,y,fixed=TRUE) & !(x==y)}))
}

openingGuide$isEndOfLine <- sapply(openingGuide$XPV,FUN=isEndOfLine)
endsOfLines <- openingGuide[openingGuide$isEndOfLine>0,]$XPV

## For each terminal move, reconstruct the line that leads to it, in
## long algebraic notation, and add a "*" character at the end to
## separate the lines as "games" in a file
allLines <- character()

for (j in 1:length(endsOfLines)) {
  aux <- openingGuide
  aux$includeInLine <- sapply(openingGuide$XPV, FUN=function(x) {
    grepl(x,endsOfLines[j],fixed=TRUE)})
  
  aux <- aux[aux$includeInLine,]
  
  aux <- aux[with(aux,order(XPV)),]
  
  allLines[[j]] <- paste(paste(aux$PV,collapse=' '),'*',sep=' ')
}


## Write the lines/games to an output file, to feed into pgn-extract
fileConn<-file(outputFilePath)
writeLines(allLines, fileConn)
close(fileConn)

Barnes’ pgn-extract utility can then convert intermediate_output.txt into a standard .pgn file final_output.pgn as follows:

pgn-extract --output final_output.pgn intermediate_output.txt

That’s all there is to it, as long as you are after just the moves, and not, say, textual comments or NAGs that were included in the opening guide. Those could be had too, by enhancing the R script above to use appropriately the NAG and COMMENT columns that are part of the GUIDE table, but I leave that to any interested party.

From a misplayed attack to a positional exchange sac

Looking at another old game I played against a fellow local club player. I played the opening in an unorthodox and clumsy way, but ended up with the chance to play a very straightforward kingside attack. At the chance to pull the trigger, I saw a ghost and bailed prematurely. On the plus side, this led to playing an interesting positional exchange sacrifice, which ultimately netted a win anyway.

“They can only take them one at a time” — Mikhail Tal

This is a game from several years ago that I played against local NM Franklin Chen. From the black side, I got myself into a cramped and delicate position. At one point, I had to find a defensive move that left 3 of my own pieces en prise, which solves all the problems I’d otherwise be facing on the board. It felt like the defensive flip side to Tal’s quip when launching attacks that involved offering multiple piece sacrifices, “They can only take them one at a time.”

After that point in the game, the tables turned a bit, and I was likely winning eventually. But I didn’t make the most of my chances, and things reached a peaceful end.

Once more unto the breach

On a whim, I decided to play in the Pittsburgh Summer Open this past weekend, having played very little chess of late, and very infrequently. I expected a great deal of rust, but I ended up playing well overall, and with a bit of luck on top I ended up scoring 2/3, and even with a performance rating (on this tiny sample of games) of 2391. The time control was G/60.

In the first round I had white against NM Nabil Feliachi. I played quietly, and figured I’d let him try to stir things up. The game was very balanced all the way to a heavy piece ending. During that final phase of the game, I played too passively and gave him a free hand trying to build up an initiative. I defended well until late in the game, when we were both getting low on time. At a certain point I was objectively lost, but swindled my way to a win.

My move 36.e4 is objectively unhelpful, and he could have simply replied 36…dxe4 and consolidated with his extra pawn. But practically, it ended up working well, as Nabil couldn’t quite switch gears from trying to attack. His response instead left the material level, and resulted in a situation where we each had dynamic possibilities. When he grabbed my a-pawn, it allowed me to create a mating net that necessitated him giving a perpetual. Unfortunately, he was still in “winning” mode, and his attempt to keep the game going ended up blundering mate in 1.

For round 2, I again had white, this time against near-master Evan Park, who is barely into double-digit age, a promising youngster who will end up very strong if he keeps at the game. The game transposed into the same position as the first game after 7.d4. Instead of Nabil’s misguided 7…Ne4, Evan played the solid 7…c6, and we had a similarly quiet game. Not too much happened through most of the game; I made motions toward building a big center, but Evan responded appropriately to keep that from happening. When he played 18…cxd4, I decided to switch positional gears by responding 19.exd4 instead of 19.Qxd4.

Both of our armies had been arranged for the position without the closed up d-file, but my sense was that my pieces were more ready to adapt to the new setting. However objectively accurate that sense was, at the very least Evan continued to spend more time thinking in the new scenario. I ended up with some nice positional chances, which I didn’t take full advantage of. We ended up in a level rook-and-bishop versus rook-and-knight endgame, where only Evan’s time trouble proved the decisive factor. He blundered a pawn, and in trying to salvage the situation with little time, ended up losing the knight instead.

So, very unexpectedly, I was on 2/2 coming into the last round, where I had to play black against the very strong NM Alex Heimann, who has a 2400+ USCF rating. We ended up in my pet Philidor Defense, and I was very happy with my position out of the opening. I mean, it was nothing special, but I had an objectively equal position with black, and one that (I thought) I was very comfortable playing. But shortly into the middlegame, I was a little too optimistic and picked the completely wrong plan (starting with 19…Bb7?), one which wasn’t tactically justified, for a reason I really had no business overlooking. It was bad enough that I was objectively lost in short order, and facing an attack that had no answer.

All in all, not a bad Saturday of chess. And in three games against master-level players, I managed to make only a handful of bad or questionable moves. I didn’t really deserve to score as well as I did, but that’s chess sometimes.