From 1adbe88f903b177294ee064a8bfd1af506ead626 Mon Sep 17 00:00:00 2001 From: Aleksey Lobanov Date: Sun, 1 Dec 2024 00:25:38 +0300 Subject: [PATCH 1/3] feat: Add different FEN approaches --- board.go | 55 +++++++++++++++++++++++++++++++++++++++++++ board_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++------- fen.go | 9 +++++++ piece.go | 9 ++++--- 4 files changed, 125 insertions(+), 13 deletions(-) diff --git a/board.go b/board.go index a61718d..7eeb3c1 100644 --- a/board.go +++ b/board.go @@ -8,6 +8,16 @@ import ( "strings" ) +var fenReplacer = strings.NewReplacer( + "11111111", "8", + "1111111", "7", + "111111", "6", + "11111", "5", + "1111", "4", + "111", "3", + "11", "2", +) + // A Board represents a chess board and its relationship between squares and pieces. type Board struct { bbWhiteKing bitboard @@ -151,6 +161,51 @@ func (b *Board) String() string { return fen } +func (b *Board) StringV2() string { + var fenBuilder strings.Builder + for r := 7; r >= 0; r-- { + for f := 0; f < numOfSquaresInRow; f++ { + sq := NewSquare(File(f), Rank(r)) + p := b.Piece(sq) + if p != NoPiece { + fenBuilder.WriteString(p.getFENChar()) + } else { + fenBuilder.WriteString("1") + } + } + if r != 0 { + fenBuilder.WriteString("/") + } + } + fen := fenBuilder.String() + for i := 8; i > 1; i-- { + repeatStr := strings.Repeat("1", i) + countStr := strconv.Itoa(i) + fen = strings.Replace(fen, repeatStr, countStr, -1) + } + return fen +} + +func (b *Board) StringV3() string { + var fenBuilder strings.Builder + for r := 7; r >= 0; r-- { + for f := 0; f < numOfSquaresInRow; f++ { + sq := NewSquare(File(f), Rank(r)) + p := b.Piece(sq) + if p != NoPiece { + fenBuilder.WriteString(p.getFENChar()) + } else { + fenBuilder.WriteString("1") + } + } + if r != 0 { + fenBuilder.WriteString("/") + } + } + + return fenReplacer.Replace(fenBuilder.String()) +} + // Piece returns the piece for the given square. func (b *Board) Piece(sq Square) Piece { for _, p := range allPieces { diff --git a/board_test.go b/board_test.go index 275988e..a141120 100644 --- a/board_test.go +++ b/board_test.go @@ -5,17 +5,66 @@ import ( ) func TestBoardTextSerialization(t *testing.T) { + for _, fen := range []string{ + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR", + "rnbqkb1r/pp1p1np1/4pp2/2pP3p/8/2N2NP1/PPP1PP1P/R1BQKBR1", + } { + t.Run(fen, func(t *testing.T) { + b := &Board{} + if err := b.UnmarshalText([]byte(fen)); err != nil { + t.Fatal("recieved unexpected error", err) + } + txt, err := b.MarshalText() + if err != nil { + t.Fatal("recieved unexpected error", err) + } + if fen != string(txt) { + t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) + } + other := b.StringV2() + if fen != string(other) { + t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) + } + + other = b.StringV3() + if fen != string(other) { + t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) + } + }) + } + +} + +func BenchmarkTextSerialization(b *testing.B) { fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" - b := &Board{} - if err := b.UnmarshalText([]byte(fen)); err != nil { - t.Fatal("recieved unexpected error", err) + board := &Board{} + if err := board.UnmarshalText([]byte(fen)); err != nil { + b.Fatal("recieved unexpected error", err) } - txt, err := b.MarshalText() - if err != nil { - t.Fatal("recieved unexpected error", err) + for i := 0; i < b.N; i++ { + board.String() + } +} + +func BenchmarkTextSerializationV2(b *testing.B) { + fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" + board := &Board{} + if err := board.UnmarshalText([]byte(fen)); err != nil { + b.Fatal("recieved unexpected error", err) + } + for i := 0; i < b.N; i++ { + board.StringV2() + } +} + +func BenchmarkTextSerializationV3(b *testing.B) { + fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" + board := &Board{} + if err := board.UnmarshalText([]byte(fen)); err != nil { + b.Fatal("recieved unexpected error", err) } - if fen != string(txt) { - t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) + for i := 0; i < b.N; i++ { + board.StringV3() } } diff --git a/fen.go b/fen.go index 62ae0cc..115deff 100644 --- a/fen.go +++ b/fen.go @@ -137,9 +137,18 @@ var ( "n": BlackKnight, "p": BlackPawn, } + pieceFenMap = reversePieceMap() fenTurnMap = map[string]Color{ "w": White, "b": Black, } ) + +func reversePieceMap() map[Piece]string { + res := make(map[Piece]string) + for k, v := range fenPieceMap { + res[v] = k + } + return res +} diff --git a/piece.go b/piece.go index 1063ba1..e0e20a8 100644 --- a/piece.go +++ b/piece.go @@ -179,10 +179,9 @@ var ( ) func (p Piece) getFENChar() string { - for key, piece := range fenPieceMap { - if piece == p { - return key - } + res, ok := pieceFenMap[p] + if !ok { + return "" } - return "" + return res } From 30a8950f11aacabe0aedaada1ba13a75a2a8b926 Mon Sep 17 00:00:00 2001 From: Aleksey Lobanov Date: Sun, 1 Dec 2024 00:28:24 +0300 Subject: [PATCH 2/3] feat: Use fastest board serialization --- board.go | 50 -------------------------------------------------- board_test.go | 33 +-------------------------------- 2 files changed, 1 insertion(+), 82 deletions(-) diff --git a/board.go b/board.go index 7eeb3c1..d99e4c8 100644 --- a/board.go +++ b/board.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/binary" "errors" - "strconv" "strings" ) @@ -138,55 +137,6 @@ func (b *Board) Draw() string { // String implements the fmt.Stringer interface and returns // a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR func (b *Board) String() string { - fen := "" - for r := 7; r >= 0; r-- { - for f := 0; f < numOfSquaresInRow; f++ { - sq := NewSquare(File(f), Rank(r)) - p := b.Piece(sq) - if p != NoPiece { - fen += p.getFENChar() - } else { - fen += "1" - } - } - if r != 0 { - fen += "/" - } - } - for i := 8; i > 1; i-- { - repeatStr := strings.Repeat("1", i) - countStr := strconv.Itoa(i) - fen = strings.Replace(fen, repeatStr, countStr, -1) - } - return fen -} - -func (b *Board) StringV2() string { - var fenBuilder strings.Builder - for r := 7; r >= 0; r-- { - for f := 0; f < numOfSquaresInRow; f++ { - sq := NewSquare(File(f), Rank(r)) - p := b.Piece(sq) - if p != NoPiece { - fenBuilder.WriteString(p.getFENChar()) - } else { - fenBuilder.WriteString("1") - } - } - if r != 0 { - fenBuilder.WriteString("/") - } - } - fen := fenBuilder.String() - for i := 8; i > 1; i-- { - repeatStr := strings.Repeat("1", i) - countStr := strconv.Itoa(i) - fen = strings.Replace(fen, repeatStr, countStr, -1) - } - return fen -} - -func (b *Board) StringV3() string { var fenBuilder strings.Builder for r := 7; r >= 0; r-- { for f := 0; f < numOfSquaresInRow; f++ { diff --git a/board_test.go b/board_test.go index a141120..10761c6 100644 --- a/board_test.go +++ b/board_test.go @@ -21,21 +21,12 @@ func TestBoardTextSerialization(t *testing.T) { if fen != string(txt) { t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) } - other := b.StringV2() - if fen != string(other) { - t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) - } - - other = b.StringV3() - if fen != string(other) { - t.Fatalf("fen expected board string %s but got %s", fen, string(txt)) - } }) } } -func BenchmarkTextSerialization(b *testing.B) { +func BenchmarkBoardTextSerialization(b *testing.B) { fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" board := &Board{} if err := board.UnmarshalText([]byte(fen)); err != nil { @@ -46,28 +37,6 @@ func BenchmarkTextSerialization(b *testing.B) { } } -func BenchmarkTextSerializationV2(b *testing.B) { - fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" - board := &Board{} - if err := board.UnmarshalText([]byte(fen)); err != nil { - b.Fatal("recieved unexpected error", err) - } - for i := 0; i < b.N; i++ { - board.StringV2() - } -} - -func BenchmarkTextSerializationV3(b *testing.B) { - fen := "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" - board := &Board{} - if err := board.UnmarshalText([]byte(fen)); err != nil { - b.Fatal("recieved unexpected error", err) - } - for i := 0; i < b.N; i++ { - board.StringV3() - } -} - func TestBoardBinarySerialization(t *testing.T) { g := NewGame() board := g.Position().Board() From cc8d800c82f1778ba96714e92955253c41415d0d Mon Sep 17 00:00:00 2001 From: Aleksey Lobanov Date: Sun, 1 Dec 2024 00:42:36 +0300 Subject: [PATCH 3/3] test: Add b.ResetTimer() for BenchmarkBoardTextSerialization --- board_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/board_test.go b/board_test.go index 10761c6..74848fe 100644 --- a/board_test.go +++ b/board_test.go @@ -32,6 +32,8 @@ func BenchmarkBoardTextSerialization(b *testing.B) { if err := board.UnmarshalText([]byte(fen)); err != nil { b.Fatal("recieved unexpected error", err) } + + b.ResetTimer() for i := 0; i < b.N; i++ { board.String() }