diff --git a/gnubg.go b/gnubg.go index 3f7ffc5..7975242 100644 --- a/gnubg.go +++ b/gnubg.go @@ -98,16 +98,16 @@ func gnubgPosition(g *bgammon.Game) string { } func parseMoves(moves []byte) ([4][2]int8, error) { + var c int // Input cursor. + var m [4][2]int8 // Parsed moves. + var mc int // Parsed moves cursor. + + // Parse moves separated by spaces. l := len(moves) - - var m [4][2]int8 - var mc int - - var i int - for i < l { + for c < l { var moveLength int - for j := i; j < l; j++ { - if moves[j] == ' ' { + for i := c; i < l; i++ { + if moves[i] == ' ' { break } moveLength++ @@ -116,20 +116,9 @@ func parseMoves(moves []byte) ([4][2]int8, error) { return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: no moves: %s", moves) } - move := moves[i : i+moveLength] + move := moves[c : c+moveLength] move = bytes.ReplaceAll(move, []byte("*"), nil) - slash := bytes.IndexRune(move, '/') - if slash == -1 { - return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid count: %s", moves) - } - nextSlash := bytes.IndexRune(move[slash+1:], '/') - var remaining []byte - if nextSlash != -1 { - remaining = move[slash+nextSlash+2:] - move = move[:slash+nextSlash+1] - } - count := 1 paren := bytes.IndexRune(move, '(') if paren != -1 { @@ -141,6 +130,17 @@ func parseMoves(moves []byte) ([4][2]int8, error) { mv := append(move[:paren], move[paren+3:]...) move = mv } + + slash := bytes.IndexRune(move, '/') + if slash == -1 { + return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid count: %s", moves) + } + nextSlash := bytes.IndexRune(move[slash+1:], '/') + var remaining []byte + if nextSlash != -1 { + remaining = move[slash+nextSlash+2:] + move = move[:slash+nextSlash+1] + } from := bgammon.ParseSpace(string(move[:slash])) if from < 0 || from > 27 { return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid from (%s): %s", move[:slash], moves) @@ -150,31 +150,33 @@ func parseMoves(moves []byte) ([4][2]int8, error) { return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid to (%s): %s", move[:slash], moves) } - for j := 0; j < count; j++ { + for i := 0; i < count; i++ { m[mc][0], m[mc][1] = int8(from), int8(to) ok := Game.AddLocalMove([]int8{int8(from), int8(to)}) if !ok { return [4][2]int8{}, fmt.Errorf("failed to add local moves (%d/%d): %s", from, to, moves) } mc++ - } - if len(remaining) > 0 { - rm, err := parseMoves(append([]byte(strconv.Itoa(int(to))+"/"), remaining...)) - if err != nil { - return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid remaining moves (%s): %s", append([]byte(strconv.Itoa(int(to))+"/"), remaining...), moves) - } - for j := 0; j < 4; j++ { - if rm[j][0] == 0 && rm[j][1] == 0 { - break + if len(remaining) > 0 { + rm, err := parseMoves(append([]byte(strconv.Itoa(int(to))+"/"), remaining...)) + if err != nil { + return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: invalid remaining moves (%s): %s", append([]byte(strconv.Itoa(int(to))+"/"), remaining...), moves) + } + for j := 0; j < 4; j++ { + if rm[j][0] == 0 && rm[j][1] == 0 { + break + } + m[mc][0], m[mc][1] = int8(rm[j][0]), int8(rm[j][1]) + mc++ } - m[mc][0], m[mc][1] = int8(rm[j][0]), int8(rm[j][1]) - mc++ } } - i += moveLength + 1 + // Advance to the next move. + c += moveLength + 1 } + // Relocate bear-off moves after moves within the board. var mb, mo [][2]int8 for j := 0; j < 4; j++ {