Skip to content
This repository has been archived by the owner on Jan 25, 2025. It is now read-only.

Commit

Permalink
Add Scanner option to expand variations (part 2 of 2)
Browse files Browse the repository at this point in the history
This commit is part of a series to add an optional ability to expand
variations when utilizing Scanner to iterate over a .pgn file.

This commit specifically implements moveListSetWithComments() when
expandVariations==true. Additionally it adds a unit test to verify
Scanner behaves as expected when ExpandVariations==true.
  • Loading branch information
mikeb26 committed May 26, 2023
1 parent 42e2e16 commit 666f43f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 1 deletion.
73 changes: 72 additions & 1 deletion pgn.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func moveListSetWithComments(pgn string, expandVariations bool) (moveListSet, er
return ret, nil
}

return ret, fmt.Errorf("unimplemented")
return moveListSetExpanded(pgn)
}

func moveListWithCommentsNoExpand(pgn string) (moveListAndOutcome, error) {
Expand Down Expand Up @@ -335,6 +335,66 @@ func moveListWithCommentsNoExpand(pgn string) (moveListAndOutcome, error) {
return ret, nil
}

var moveNumRe = regexp.MustCompile(`(?:\d+\.+)?(.*)`)

func moveListSetExpanded(pgn string) (moveListSet, error) {
firstGame := moveListAndOutcome{
moves: []moveWithComment{},
}
ret := moveListSet{
moveLists: []moveListAndOutcome{firstGame},
}

pgn = stripTagPairs(pgn)
// remove comments @todo need to add comments back in
pgn = removeSection("{", "}", pgn)
// remove line breaks
pgn = strings.Replace(pgn, "\n", " ", -1)
pgn = strings.ReplaceAll(pgn, "(", "( ")
pgn = strings.ReplaceAll(pgn, ")", " )")

moveListIdx := 0
moveListIdxStack := make([]int, 0)
list := strings.Split(pgn, " ")

for _, move := range list {
move = strings.TrimSpace(move)
switch move {
case string(NoOutcome), string(WhiteWon), string(BlackWon), string(Draw):
ret.moveLists[moveListIdx].outcome = Outcome(move)
case "":
case "(":
// begin new variation
moveListIdxStack = append(moveListIdxStack, moveListIdx)
newIdx := len(ret.moveLists)
numMoves := len(ret.moveLists[moveListIdx].moves) - 1
newGame := moveListAndOutcome{}
newGame.moves = make([]moveWithComment, numMoves)
copy(newGame.moves, ret.moveLists[moveListIdx].moves)
ret.moveLists = append(ret.moveLists, newGame)
moveListIdx = newIdx

case ")":
// end current variation
stackSize := len(moveListIdxStack)
if stackSize == 0 {
return ret, fmt.Errorf("Failed to parse variation")
}
moveListIdx = moveListIdxStack[stackSize-1]
moveListIdxStack = moveListIdxStack[:stackSize-1]
default:
results := moveNumRe.FindStringSubmatch(move)
tmp := moveWithComment{}
if len(results) == 2 && results[1] != "" {
tmp.MoveStr = results[1]
ret.moveLists[moveListIdx].moves = append(ret.moveLists[moveListIdx].moves, tmp)
}
}
}

return ret, nil
}

func stripTagPairs(pgn string) string {
lines := strings.Split(pgn, "\n")
cp := []string{}
Expand Down Expand Up @@ -386,3 +446,14 @@ func stripVariations(pgn string) (string, error) {

return ret.String(), nil
}

func removeSection(leftChar, rightChar, s string) string {
r := regexp.MustCompile(leftChar + ".*?" + rightChar)
for {
i := r.FindStringIndex(s)
if i == nil {
return s
}
s = s[0:i[0]] + s[i[1]:]
}
}
35 changes: 35 additions & 0 deletions pgn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,41 @@ func TestScannerWithNested(t *testing.T) {
}
}

func TestScannerWithNestedAndExpand(t *testing.T) {
fname := "fixtures/pgns/0013.pgn"
f, err := os.Open(fname)
if err != nil {
panic(err)
}
defer f.Close()

scannerOpts := ScannerOpts{ExpandVariations: true}
scanner := NewScannerWithOptions(f, scannerOpts)
games := []*Game{}
for scanner.Scan() {
err = scanner.Err()
if err != nil && err != io.EOF {
t.Fatalf(fname+" Unexpected non-nil/non-EOF err %v", err)
}
game := scanner.Next()
moveList := game.Moves()
if len(moveList) == 0 {
continue
}
games = append(games, game)
}
err = scanner.Err()
if err != io.EOF {
t.Fatalf(fname+" Unexpected non-EOF err %v", err)
}
if len(games) != 10 {
for idx, g := range games {
fmt.Printf("Parsed game %v: %v\n\n", idx, g)
}
t.Fatalf(fname+" expected 10 games but got %d", len(games))
}
}

func BenchmarkPGN(b *testing.B) {
pgn := mustParsePGN("fixtures/pgns/0001.pgn")
b.ResetTimer()
Expand Down

0 comments on commit 666f43f

Please sign in to comment.