notnil / chess Goto Github PK
View Code? Open in Web Editor NEWchess package for go
License: MIT License
chess package for go
License: MIT License
Hi,
I'm writing to extend a huge thanks for this module and to let you know that I've just released a terminal based chess game that makes heavy use of notnil/chess. Your module was hugely helpful in permitting me to focus on the core functionality of the game rather than dealing with all of the idiosyncrasies of chess rules. Thank you!
Hi, it appears that stockfish uses 1 thread by default. How would I set the UCI thread option?
I've tried manually changing engine.New as follows but it didn't make any difference
func New(path string, opts ...func(e *Engine)) (*Engine, error) {
path, err := exec.LookPath(path)
if err != nil {
return nil, fmt.Errorf("uci: executable not found at path %s %w", path, err)
}
rIn, wIn := io.Pipe()
rOut, wOut := io.Pipe()
cmd := exec.Command(path)
cmd.Stdin = rIn
cmd.Stdout = wOut
var options = make(map[string] Option)
options["Threads"] = Option{
Name: "Threads",
Type: OptionSpin,
Default: "2",
Min: "2",
Max: "4",
Vars: []string{},
}
e := &Engine{cmd: cmd, in: wIn, out: rOut, mu: &sync.RWMutex{}, logger: log.New(os.Stdout, "uci", log.LstdFlags), options: options}
for _, opt := range opts {
opt(e)
}
go e.cmd.Run()
return e, nil
}
Hi !
Learning chess and go at the same time, I'm trying to build a small game for myself to practice on, and found your library, which looks very interesting, thank you for building it!
I have a small question, this is my code in my game loop:
game = chess.NewGame()
squares := game.Position().Board().SquareMap()
for square, _ := range squares {
color := square.color() // ???
}
You can see what problem I have here. I would like my UI is as "dumb" as possible and rely on your library for all the logic, but unfortunately I cannot get a square's color.
Is there another way to achieve this ? Or would making the Color() function publicly available be the way to do this ?
I can look at https://github.com/notnil/chessimg/blob/master/chessimg.go#L124, but obviously here making the Color() function public could be helpful as well there.
Thanks !
First of all, great work with the library!
One issue (or a missing feature) I encountered: it does not handle Chess960 castling correctly.
E.g.
package main
import (
"fmt"
"github.com/notnil/chess"
)
func main() {
fen, _ := chess.FEN("bnqbnrkr/pppppppp/8/8/8/8/PPPPPPPP/BNQBNRKR w KQkq - 0 1")
game := chess.NewGame(chess.UseNotation(chess.UCINotation{}), fen)
game.MoveStr("b1a3")
game.MoveStr("f7f6")
game.MoveStr("f2f4")
game.MoveStr("f6f5")
game.MoveStr("f1f3")
game.MoveStr("g7g6")
game.MoveStr("g1g1") // King-side castling
// print outcome and game PGN
fmt.Println(game.Position().Board().Draw())
fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
fmt.Println(game.String())
}
The PGN does not have the castling move present: 1. b1a3 f7f6 2. f2f4 f6f5 3. f1f3 g7g6 *
Same with the printed board.
Using X-FEN (here bnqbnrkr/pppppppp/8/8/8/8/PPPPPPPP/BNQBNRKR w HFhf - 0 1
) to encode the starting position does not help.
Are there any plans to support Chess960?
@sumnerevans I found a parsing issue with the latest regex code. It can't parse the following pgn:
[Event "Rated Blitz game"]
[Site "https://lichess.org/JXpwpOJf"]
[White "georgekontos"]
[Black "zev105"]
[Result "1-0"]
[UTCDate "2014.08.31"]
[UTCTime "22:03:44"]
[WhiteElo "1292"]
[BlackElo "1429"]
[WhiteRatingDiff "+15"]
[BlackRatingDiff "-15"]
[ECO "C42"]
[Opening "Russian Game: Three Knights Game"]
[TimeControl "300+3"]
[Termination "Normal"]
1. e4 e5 2. Nf3 Nf6 3. Nc3 Bc5 4. Nxe5 Qe7 5. Bd3 Qxe5 6. O-O O-O 7. a3 d6 8. b4 Bd4 9. Bb2 Nbd7 10. Bc4 Nb6 11. Qe2 Nxc4 12. Qxc4 Be6 13. Qxc7 Nxe4 14. Nxe4 Bxb2 15. Rae1 Rac8 16. Qxd6 Bc4 17. Qxe5 Bxe5 18. d3 Be6 19. Nc5 Bxh2+ 20. Kxh2 Bd5 21. c4 Bc6 22. Re7 Rcd8 23. Rd1 b6 24. b5 Bxb5 25. cxb5 bxc5 26. Rxa7 Rd6 27. Rc7 Rh6+ 28. Kg1 Re8 29. Rxc5 f5 30. b6 f4 31. b7 Rb8 32. Rc8+ Rxc8 33. bxc8=Q+ Kf7 34. Qf5+ Rf6 35. Qxh7 f3 36. g3 Ra6 37. Qh5+ g6 38. Qh7+ Kf6 39. Qh8+ Kg5 40. Re1 Rxa3 41. Re5+ Kg4 42. Qh4# 1-0
I'm having trouble reasoning about the long regex. Any way you can take a look?
As best I can tell, this is nonstandard. So while decode can probably support it, encode should not output it.
Right now you have to query every aspect of the game separately:
The package needs an iterator or combined game history list that ties all of these elements together.
I'm using the UCI library to explore certain chess positions. AFAICT, the SearchResults
populator doesn't respect MultiPV search, and will give simply the most recent info-line in the SearchResults().Info
. Would it be possible to consume Info
as an array, or perhaps have a separate variable in SearchResults
called MultiPVInfo
?
Hello! I am trying to generate a flipped SVG file for the black player.
I write code like this:
board := pos.Board()
if pos.Turn() == chess.Black {
board = board.Flip(chess.UpDown).Flip(chess.LeftRight)
}
if err := image.SVG(f, board); err != nil {
log.Fatal(err)
}
But I got this, where the a1 should be at the left top
What should I do to generate a correct SVG file? Or is this a bug?
I'm trying to specify SearchMoves for the CmdGo struct in the UCI package, but SearchMoves expects []*chess.Move for its value. I can't find a way to construct a Move from outside of the chess package, as the Move structs members are all private. You have some boilerplate that is close to what I need via the strToSquareMap var; however, I'd need to cargo that into my code, as it is private as well, and that still doesn't solve the issue that the chess.Move members are private.
Can you tell me how to construct a Move from outside of the chess package? If that's not possible, can a constructor be added to make this possible?
Support for comments isn't mentioned in the readme and doesn't have examples. This should be remedied.
Expected result: Game completed. 1-0 by Checkmate.
Actual result: Game completed. 1/2-1/2 by Stalemate.
package main
import (
"fmt"
"math/rand"
"github.com/notnil/chess"
)
func main() {
fen, _ := chess.FEN("rn1qkbnr/pbpp1Qpp/1p6/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1")
game := chess.NewGame(fen)
// generate moves until game is over
for game.Outcome() == chess.NoOutcome {
// select a random move
moves := game.ValidMoves()
move := moves[rand.Intn(len(moves))]
game.Move(move)
}
// print outcome and game PGN
fmt.Println(game.Position().Board().Draw())
fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
fmt.Println(game.String())
}
I am trying to find the first queen move in a game. Looping over the Move slice returned by game.Moves()
seemed like a good idea, but I don't find a way to correlate the move to the piece being on the start square before or the target square after it was executed. Is there a good way to do this using the Positions data?
Thank you for any pointer to the right direction here :)
Hi, it would be really interesting if you could provide PERFT results in the README file.
Implementing UCI communication for chess engines would be a great improvement. Could be worth adding it if possible.
Right now there are no license details in the repo, whether MIT, GPL or Apache. Would appreciate a clarification.
I would like to use this Go library for an AlphaBeta pruning look ahead algorithm, but I don't understand how to neatly undo a move. Say we pushed a move to a game like so
game := chess.NewGame()
moves := game.ValidMoves()
game.Move(moves[0])
How can we then undo the last move that was made?
Here is a reference from the code.
// Move updates the game with the given move. An error is returned
// if the move is invalid or the game has already been completed.
func (g *Game) Move(m *Move) error {
// TODO there is a bug here. The move pointer shouldn't
// be used directly. It could be later set to nil or
// it might have incomplete tag information from
// encodings.
if !moveSlice(g.ValidMoves()).contains(m) {
return fmt.Errorf("chess: invalid move %s", m)
}
g.moves = append(g.moves, m)
g.pos = g.pos.Update(m)
g.positions = append(g.positions, g.pos)
g.updatePosition()
return nil
}
We can't trust moves coming from previous valid move calls or encoded directly with a notation. Also we aren't checking for nil.
Anyone of the options can be nil
// NewGame defaults to returning a game in the standard
// opening position. Options can be given to configure
// the game's initial state.
func NewGame(options ...func(*Game)) *Game {
pos, _ := decodeFEN(startFEN)
game := &Game{
notation: AlgebraicNotation{},
moves: []*Move{},
pos: pos,
positions: []*Position{pos},
outcome: NoOutcome,
method: NoMethod,
}
for _, f := range options {
f(game)
}
return game
}
Pawn on e6 moved to e5 capturing my Bishop. Pawns should not be able to capture without moving diagonally.
2b1r3/2k2pBB/p2np3/8/8/5N2/PP1K1PPP/3R4 w - - 1 1
Enter Move in Algebraic Notation (ex. 'e4'):
Be5
A B C D E F G H
8- - ♝ - ♜ - - -
7- - ♚ - - ♟ - ♗
6♟ - - ♞ ♟ - - -
5- - - - ♗ - - -
4- - - - - - - -
3- - - - - ♘ - -
2♙ ♙ - ♔ - ♙ ♙ ♙
1- - - ♖ - - - -
2b1r3/2k2p1B/p2np3/4B3/8/5N2/PP1K1PPP/3R4 b - - 2 1
A B C D E F G H
8- - ♝ - ♜ - - -
7- - ♚ - - ♟ - ♗
6♟ - - ♞ - - - -
5- - - - ♟ - - -
4- - - - - - - -
3- - - - - ♘ - -
2♙ ♙ - ♔ - ♙ ♙ ♙
1- - - ♖ - - - -
2b1r3/2k2p1B/p2n4/4p3/8/5N2/PP1K1PPP/3R4 w - - 0 1
Enter Move in Algebraic Notation (ex. 'e4'):
I'm trying to implement a bot with this library powering the game. However, instead of deep copying the board while constructing the tree there should be a method to pop the last move off the MoveHistory as it would be a lot faster than copying the board for each node of the minimax tree.
Is there a function that exists currently?
`
func main() {
pgn, err := chess.PGN(strings.NewReader("\n1. d4 d5 2. Bf4 Nc6 3. e3 Qd6 4. Bxd6 exd6 5. Nc3 Nf6 6. Qf3 Bg4 7. Qf4 O-O-O 8. Nb1 Nb4 9. c3 Nxa2 10. Rxa2 Re8 11. f3 Bd7 12. h3 Nh5 13. Qxf7 Rxe3+ 14. Be2 Ng3 15. b4 Nxh1 16. Rxa7 Ng3 17. Ra8# 1-0"))
if err != nil {
fmt.Println(err.Error())
}
game := chess.NewGame(pgn)
a := game.String()
fmt.Println(a)
_, err = chess.PGN(strings.NewReader(a))
fmt.Println(err.Error())
}
`
now, its output:
`
1.d4 d5 2.Bf4 Nc6 3.e3 Qd6 4.Bxd6 exd6 5.Nc3 Nf6 6.Qf3 Bg4 7.Qf4 O-O-O 8.Nb1 Nb4 9.c3 Nxa2 10.Rxa2 Re8 11.f3 Bd7 12.h3 Nh5 13.Qxf7 Rxe3+ 14.Be2 Ng3 15.b4 Nxh1 16.Rxa7 Ng3 17.Ra8# 1-0
chess: pgn decode error chess: failed to decode notation text "1.d4" for position rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 on move 1
`
Here is an excerpt from the game I was playing against my AI. Notice how the KQkq in the FEN is missing after the second move.
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
Enter Move in Algebraic Notation (ex. 'e4'):
e4
A B C D E F G H
8♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
7♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
6- - - - - - - -
5- - - - - - - -
4- - - - ♙ - - -
3- - - - - - - -
2♙ ♙ ♙ ♙ - ♙ ♙ ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1
A B C D E F G H
8♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
7♟ ♟ - ♟ ♟ ♟ ♟ ♟
6- - - - - - - -
5- - ♟ - - - - -
4- - - - ♙ - - -
3- - - - - - - -
2♙ ♙ ♙ ♙ - ♙ ♙ ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w - c6 0 1
On the website The Week in Chess (https://theweekinchess.com/twic) there is an archive of PGN file.
I created some functions that would create a large pgn from all or parts of the archive.
If you are interested to adding to this project i can make them available to this library
I am parsing the 2013-01 database file from Lichess (15Mb download), and encountered the following error:
chess: pgn decode error chess: could not decode algebraic notation Rd1d2 for position 3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PP1QPP1/3R3K w - - 2 22 on move 22
The typical short notation would be R1d2
, which is acceptable without error, but the long form error not accepted.
How to Reproduce
PGN:
[Event "Rated Blitz game"]
[Site "https://lichess.org/9opx3qh7"]
[White "adamsrj"]
[Black "hamiakaz"]
[Result "0-1"]
[UTCDate "2012.12.31"]
[UTCTime "23:02:48"]
[WhiteElo "1522"]
[BlackElo "1428"]
[WhiteRatingDiff "-14"]
[BlackRatingDiff "+14"]
[ECO "A40"]
[Opening "Englund Gambit Complex: Hartlaub-Charlick Gambit"]
[TimeControl "180+5"]
[Termination "Normal"]
1. d4 e5 2. dxe5 d6 3. exd6 Bxd6 4. Nf3 Nf6 5. Nc3 O-O 6. a3 Nc6 7. e3 a6 8. Be2 h6 9. O-O Ne5 10. Bd2 Nxf3+ 11. Bxf3 Be5 12. Rc1 c6 13. Qe2 Qd6 14. Rfd1 Bxh2+ 15. Kh1 Be5 16. e4 Bxc3 17. Bxc3 Qe6 18. Rd3 Bd7 19. Rcd1 Rad8 20. Bxf6 gxf6 21. Rd6 Qe7 22. Rd1d2 Be6 23. Rxd8 Rxd8 24. Rxd8+ Qxd8 25. c4 Qd4 26. c5 Qxc5 27. Qd2 f5 28. exf5 Bxf5 29. Qxh6 Bg6 30. Be4 Bxe4 31. Qh4 Bg6 32. Qd8+ Kg7 33. Qc7 b5 34. b4 Qc1+ 35. Kh2 Qxa3 36. Qe5+ Kg8 37. Qe8+ Kg7 38. Qxc6 Qxb4 39. Qxa6 Qh4+ 40. Kg1 b4 41. Qa1+ Qf6 42. Qa4 Qc3 43. f3 b3 44. Qa3 Qc2 45. Kh2 b2 0-1
I've confirmed the test failed by adding this line to assets/valid_notation_tests.json
:
{
"pos1" : "3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PP1QPP1/3R3K w - - 2 22",
"pos2" : "3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PPRQPP1/7K b - - 3 22",
"algText" : "R1d2",
"longAlgText" : "Rd1d2",
"description" : "https://lichess.org/editor/3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PP1QPP1/3R3K_w_-_-_2_22"
}
Test Output:
--- FAIL: TestValidDecoding (0.01s)
--- FAIL: TestValidDecoding/https://lichess.org/editor/3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PPRQPP1/7K_w_-_-_2_22 (0.00s)
notation_test.go:45: starting from board
A B C D E F G H
8- - - ♜ - ♜ ♚ -
7- ♟ - ♝ ♛ ♟ - -
6♟ - ♟ ♖ - ♟ - ♟
5- - - - - - - -
4- - - - ♙ - - -
3♙ - - - - ♗ - -
2- ♙ ♙ - ♕ ♙ ♙ -
1- - - ♖ - - - ♔
expected move to be valid error - chess: failed to decode long algebraic notation text "Rd1d2" for position 3r1rk1/1p1bqp2/p1pR1p1p/8/4P3/P4B2/1PP1QPP1/3R3K w - - 2 22 h1g1,h1h2,e2e1,e2f1,e2d2,e2d3,e2e3,e2c4,e2b5,e2a6,d1a1,d1b1,d1c1,d1e1,d1f1,d1g1,d1d2,d1d3,d1d4,d1d5,d6d2,d6d3,d6d4,d6d5,d6c6,d6e6,d6f6,d6d7,f3g4,f3h5,b2b3,b2b4,c2c3,c2c4,g2g3,g2g4,a3a4,e4e5
FAIL
exit status 1
FAIL github.com/notnil/chess 2.767s
When reading a large PGN file, I stumbled across this error. Looks like it's a problem with the disambiguation:
chess: pgn decode error chess: could not decode algebraic notation Q1f1 for position 2k5/6K1/8/8/8/8/4qq2/4q3 b - - 9 55 on move 55
To reproduce:
func TestMoveHistory(t *testing.T) {
game := chess.NewGame()
game.MoveStr("e4")
game.MoveStr("e5")
game.Resign(chess.Black)
history := game.MoveHistory() // Panics here
if len(history) != 2 {
t.Fatal("Didn't retrieve full history")
}
}
That there's also no way to make a move with comments.... well, I guess that's #89
Eg. 8/8/8/8/4k3/8/3K2p1/5r2 w - - 50 119
should not be a draw, but is with the current rules.
Quote from wikipedia on this:
The fifty-move rule in chess states that a player can claim a draw if no capture has been made and no pawn has been moved in the last fifty moves (for this purpose a "move" consists of a player completing their turn followed by the opponent completing their turn).
For example, at the end of the game, I want to know how many white pawns were destroyed by the black side
6Q1/5R2/8/6k1/P7/8/8/5K2 b - - 8 66
Hi.
Writing a UI using this library, and I'm running into a smallish issue, maybe you have a solution for me!
In my game, whenever you drag one of your pieces around, it will highlight the squares you can move it to.
When it's the opponent's turn, I can still drag my pieces around (but not drop them obviously), and I'd like to still highlight the possible moves for that piece while my opponent is thinking.
Using the previous position works great, however I can't get the "new" valid moves from the piece I just moved. It'll be easier to show screenshots of what I mean.
This is the position before I make move, you can see the white king is being dragged around and the squares where I can move my king are highlighted
https://jpleau.ca/before_move.png
This is the position after I move (and it's now my opponent's turn). My king is being dragged around. I would like to get the possible moves for my king, but ValidMoves() only lists moves for black until they make their move.
https://jpleau.ca/after_move.png
Is there something I can do to list the valid moves for the opposite color of Position().Turn() ? I was thinking being able to call Position().Update(nil) to simulate a no-op move for the current player, and so I could use that position to list the valid moves I need. I have no idea of the feasibility of this, as adding invalid moves could break things...
Do you have a suggestion for me ?
Thanks !
Having some issues in creating a game using game.moveStr() as there seems no way to set the Outcome of the game other than if the last move made is mate (#). So far the only way I see of setting the result is to use game.PGN and read all the moves + result and then newGame(pgn).
As you have methods to enter individual moves am I missing something to set the result/Outcome ?
Thanks!
OK just realized that you have Resign method.....please ignore.
Hi there!
I'm the author of kgigitdev/godgt, which I'm currently rewriting (not uploaded yet) to use your excellent library for legal move generation.
godgt is a command line utility to record games from DGT's electronic chess boards.
I was wondering if you would be open to exporting some of the currently non-exported methods in your library.
The DGT board generates low-level piece events ("e4 is now empty", "d5 now contains a white pawn"), which due to the speed and order in which the squares are scanned, you can sometimes receive out of order.
My (probably inefficient) technique is therefore to ask for a complete board dump every time I receive an event, then compare that to every legal next position from the most recently accepted legal move.
One big problem I have is sliding piece moves. For example, when executing a "Bb5" move, a player might slide the bishop over d2, e3 and c4, and if the board happens to scan the piece in that position, then Bd3/Be4/Bd4 becomes the accepted move, and the later "Bb5" is rejected as being an invalid move (because the same player is seemingly playing twice).
One workaround might be to store the previous game state (the state immediately before the most recently accepted legal move) and also see if the new position is a legal move in that game state as well.
But that approach requires the ability to copy a game object. Game does goes have an unexported copy() method, but that's obviously not available.
There are a handful of other methods that are in a "would be nice to export" category, but which can be worked around. I can't recall them at the moment.
If you're open to such changes, let me know, and I'll see if I can get together a PR.
I'd like to use this library to read really large PGN files. There's two suggestions I'd like to make to improve this. If you agree with my suggestions, then I'm happy to have a crack at implementing them:
Creation of a Game from PGN fails if it doesn't start from initial position. If FEN tag is present in PGN game description, it should be used to init a game.
Example.
Parsing of pgn with content:
[Site "?"]
[Date "????.??.??"]
[Round "?"]
[White "N.N."]
[Black "N.N."]
[Result "1-0"]
[Annotator "T1R"]
[SetUp "1"]
[FEN "2r2rk1/pp1bqpp1/2nppn1p/2p3N1/1bP5/1PN3P1/PBQPPPBP/3R1RK1 w - - 0 1"]
[PlyCount "5"]
[EventType "swiss"]
1. Nd5 exd5 (1... hxg5 2. Nxe7+ Nxe7) 2. Bxf6 hxg5 3. Bxe7 1-0
returns error: chess: pgn decode error chess: could not decode algebraic notation Nd5 for position rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 on move 1
Need PGN tests for every notation.
Here is a pull request I made to add more test in ThreeFoldRepetition in the file "game_test.go"
note that I also changed "repition" to "repetition"
// test Threefold repetition
func TestThreeFoldRepetition(t *testing.T) {
testCases := []struct {
name string
moves []string
want bool
}{
{"knight back home one times", []string{
"Nf3", "Nf6", "Ng1", "Ng8",
}, false},
{"knight back home two times", []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}, true},
{"change king side castle right", []string{
"e4", "e5", "Bc4", "Bc5",
"Nf3", "Nf6",
"Rf1", "Rf8", "Rh1", "Rh8",
"Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}, false},
{"change en-passant right", []string{
"e4", "e6", "e5", "d5",
"Nf3", "Ne7", "Ng1", "Ng8",
"Nf3", "Ne7", "Ng1", "Ng8",
}, false},
{"change side to move", []string{
"e4", "e5",
"Nf3", "Nf6", "Ng1", "Ng8",
"Bb5", "Nf6", "Bc4", "Ng8",
"Bf1",
}, false},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%s=>%t", tc.name, tc.want), func(t *testing.T) {
g := NewGame()
for _, m := range tc.moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if err := g.Draw(ThreefoldRepetition); (tc.want == true && err != nil) || (tc.want == false && err == nil) {
for _, pos := range g.Positions() {
log.Println(pos.String())
}
t.Fatalf("%s - %d reps", err.Error(), g.numOfRepetitions())
}
})
}
}
Reading this PGN:
[Event "self-play-1"]
[Site "?"]
[Date "2021.09.06"]
[Round "98"]
[White "zahak-linux-amd64-6.2"]
[Black "zahak-linux-amd64-6.2"]
[Result "0-1"]
[FEN "r1bqkbnr/1pppp1pp/5p2/p3n3/P1P3PP/8/1P1PPP2/RNBQKBNR w KQkq - 0 1"]
[GameDuration "00:00:02"]
[GameEndTime "2021-09-06T17:23:16.165 EDT"]
[GameStartTime "2021-09-06T17:23:14.028 EDT"]
[PlyCount "77"]
[SetUp "1"]
[Termination "adjudication"]
[TimeControl "inf"]
1. Nf3 {-0.17/9 0.041s} Nxc4 {+0.09/9 0.023s} 2. Nc3 {-0.23/9 0.020s}
d5 {+0.12/9 0.017s} 3. d3 {-0.42/9 0.019s} Ne5 {+0.38/9 0.012s}
4. Nxe5 {-0.02/9 0.016s} fxe5 {+0.28/9 0.023s} 5. g5 {-0.12/9 0.033s}
e6 {+0.30/9 0.042s} 6. h5 {-0.55/9 0.085s} Bc5 {+0.48/9 0.045s}
7. h6 {-0.67/9 0.077s} gxh6 {+0.92/9 0.019s} 8. e4 {-0.30/9 0.027s}
d4 {+0.90/9 0.052s} 9. Na2 {-0.67/9 0.017s} Kd7 {+0.12/9 0.039s}
10. gxh6 {-0.30/9 0.028s} Nf6 {+0.29/9 0.023s} 11. Rg1 {-0.05/9 0.015s}
Rg8 {+0.31/9 0.020s} 12. Rg7+ {-0.21/9 0.017s} Rxg7 {-0.03/9 0.078s}
13. hxg7 {-0.07/9 0.016s} Qe7 {+0.20/9 0.014s} 14. Bh6 {-0.37/9 0.034s}
b6 {+0.20/9 0.028s} 15. Qd2 {-0.49/9 0.047s} Qf7 {+0.53/9 0.062s}
16. Be2 {-1.03/9 0.15s} Ng8 {+0.87/9 0.038s} 17. Qc1 {-1.23/9 0.037s}
Qf6 {+1.76/9 0.010s} 18. Bg5 {-1.53/9 0.027s} Qxg7 {+1.64/9 0.010s}
19. Bh4 {-1.85/9 0.034s} Bb7 {+2.01/9 0.013s} 20. Bg3 {-1.83/9 0.034s}
Ne7 {+1.96/9 0.033s} 21. Bf1 {-2.08/9 0.048s} Ng6 {+2.28/9 0.031s}
22. b3 {-2.65/9 0.052s} Rg8 {+2.68/9 0.026s} 23. Bh3 {-2.61/9 0.058s}
Ba6 {+3.02/9 0.030s} 24. Bf1 {-3.14/9 0.022s} h5 {+3.10/9 0.014s}
25. Bh2 {-3.24/9 0.013s} h4 {+3.24/9 0.012s} 26. Qd2 {-2.91/9 0.018s}
Qf8 {+3.32/9 0.015s} 27. Qg5 {-3.59/9 0.031s} Bb4+ {+4.06/9 0.013s}
28. Nxb4 {-3.54/9 0.012s} Qxb4+ {+3.53/9 0.018s} 29. Qd2 {-3.48/9 0.017s}
Qxb3 {+3.53/9 0.007s} 30. Rc1 {-3.55/9 0.015s} c6 {+3.73/9 0.014s}
31. Qc2 {-3.67/9 0.020s} Qxc2 {+3.32/9 0.008s} 32. Rxc2 {-3.63/9 0.007s}
Nf4 {+3.53/9 0.011s} 33. Bxf4 {-3.74/9 0.009s} exf4 {+4.21/9 0.006s}
34. Ke2 {-3.77/9 0.006s} Rg1 {+3.76/9 0.008s} 35. Bh3 {-4.45/9 0.014s}
Ra1 {+4.52/9 0.019s} 36. e5 {-4.63/9 0.027s} Ra3 {+4.49/9 0.014s}
37. Rd2 {-4.74/9 0.021s} Rxa4 {+4.70/9 0.003s} 38. Kf3 {-4.52/9 0.006s}
Ra3 {+4.58/9 0.015s} 39. Bf1 {-4.55/9 0.009s, Black wins by adjudication} 0-1
Will result in this error:
panic: chess: pgn decode error chess: failed to decode notation text "{+0.40/9" for position rnbqkbnr/2pp1p1p/1p2p3/p5p1/PP6/N6N/1BPPPPPP/R2QKB1R b KQkq - 1 1 on move 1
goroutine 1 [running]:
main.main()
/home/amanj/Documents/fengen/fengen.go:47 +0xbfa
I am trying to parse this World Championship game
[Event "WCh"]
[Site "Elista RUS"]
[Date "2006.10.05"]
[Round "8"]
[White "Kramnik,V"]
[Black "Topalov,V"]
[Result "0-1"]
[WhiteElo "2743"]
[BlackElo "2813"]
[EventDate "2006.09.23"]
[ECO "D47"]
1. d4 d5 2. c4 c6 3. Nf3 Nf6 4. Nc3 e6 5. e3 Nbd7 6. Bd3 dxc4 7. Bxc4 b5 8.
Be2 Bb7 9. O-O b4 10. Na4 c5 11. dxc5 Nxc5 12. Bb5+ Ncd7 13. Ne5 Qc7 14.
Qd4 Rd8 15. Bd2 Qa5 16. Bc6 Be7 17. Rfc1 Bxc6 18. Nxc6 Qxa4 19. Nxd8 Bxd8
20. Qxb4 Qxb4 21. Bxb4 Nd5 22. Bd6 f5 23. Rc8 N5b6 24. Rc6 Be7 25. Rd1 Kf7
26. Rc7 Ra8 27. Rb7 Ke8 28. Bxe7 Kxe7 29. Rc1 a5 30. Rc6 Nd5 31. h4 h6 32.
a4 g5 33. hxg5 hxg5 34. Kf1 g4 35. Ke2 N5f6 36. b3 Ne8 37. f3 g3 38. Rc1
Nef6 39. f4 Kd6 40. Kf3 Nd5 41. Kxg3 Nc5 42. Rg7 Rb8 43. Ra7 Rg8+ 44. Kf3
Ne4 45. Ra6+ Ke7 46. Rxa5 Rg3+ 47. Ke2 Rxe3+ 48. Kf1 Rxb3 49. Ra7+ Kf6 50.
Ra8 Nxf4 51. Ra1 Rb2 52. a5 Rf2+ 0-1
But it results in this:
chess: pgn decode error chess: failed to decode notation text "N5f6" for position r7/1R1nk3/2R1p3/p2n1p2/P5p1/4P3/1P2KPP1/8 b - - 1 35 on move 35
I assume it might be a problem because of that slightly uncommon case where the 5 is required to distinguish the knights on the same file when both could jump to f6.
Another World Championship example that fails to parse:
chess: pgn decode error chess: failed to decode notation text "Ndxb5" for position rn2k2r/pp3ppp/4pB2/qb6/1b1NP3/2N5/PP3PPP/R2QK2R w KQkq - 0 11 on move 11
Parsing and validating algebraic notation is slow and certain applications could benefit from faster encoding / decoding.
The array that starts with a7a6 represents valid moves, but you have to dig into the code to figure that out. The list should either be removed or labeled.
chess: could not decode algebraic notation c6��� for position rnbqkbnr/pppppppp/8/8/
1P6/8/P1PPPPPP/RNBQKBNR b KQkq b3 0 1 [a7a6 b7b6 c7c6 d7d6 e7e6 f7f6 g7g6 h7h6 a7a5 b7b5 c7c5 d7d5 e7e5 f7f5 g7g5 h7h
5 b8c6 g8h6 b8a6 g8f6] on move 1 - Tag Pairs: []
It would be good to implement the BinaryMarshaler / BinaryUnmarshaler interfaces for game, position & moves for the efficient storage / transmission of data. If you are happy with the idea, I'd be happy to have a go at implementing it.
This method returns the letter for every other piece.
The Game struct is not writing out comments back out in its PGN. Input of
[Event "Rated Bullet tournament https://lichess.org/tournament/yc1WW2Ox"]
[Site "https://lichess.org/PpwPOZMq"]
[Date "2017.04.01"]
[Round "-"]
[White "Abbot"]
[Black "Costello"]
[Result "0-1"]
[UTCDate "2017.04.01"]
[UTCTime "11:32:01"]
[WhiteElo "2100"]
[BlackElo "2000"]
[WhiteRatingDiff "-4"]
[BlackRatingDiff "+1"]
[WhiteTitle "FM"]
[ECO "B30"]
[Opening "Sicilian Defense: Old Sicilian"]
[TimeControl "300+0"]
[Termination "Time forfeit"]
1. e4 { [%eval 0.17] [%clk 0:00:30] } 1... c5 { [%eval 0.19] [%clk 0:00:30] }
2. Nf3 { [%eval 0.25] [%clk 0:00:29] } 2... Nc6 { [%eval 0.33] [%clk 0:00:30] }
3. Bc4 { [%eval -0.13] [%clk 0:00:28] } 3... e6 { [%eval -0.04] [%clk 0:00:30] }
4. c3 { [%eval -0.4] [%clk 0:00:27] } 4... b5? { [%eval 1.18] [%clk 0:00:30] }
5. Bb3?! { [%eval 0.21] [%clk 0:00:26] } 5... c4 { [%eval 0.32] [%clk 0:00:29] }
6. Bc2 { [%eval 0.2] [%clk 0:00:25] } 6... a5 { [%eval 0.6] [%clk 0:00:29] }
7. d4 { [%eval 0.29] [%clk 0:00:23] } 7... cxd3 { [%eval 0.6] [%clk 0:00:27] }
8. Qxd3 { [%eval 0.12] [%clk 0:00:22] } 8... Nf6 { [%eval 0.52] [%clk 0:00:26] }
9. e5 { [%eval 0.39] [%clk 0:00:21] } 9... Nd5 { [%eval 0.45] [%clk 0:00:25] }
10. Bg5?! { [%eval -0.44] [%clk 0:00:18] } 10... Qc7 { [%eval -0.12] [%clk 0:00:23] }
11. Nbd2?? { [%eval -3.15] [%clk 0:00:14] } 11... h6 { [%eval -2.99] [%clk 0:00:23] }
12. Bh4 { [%eval -3.0] [%clk 0:00:11] } 12... Ba6? { [%eval -0.12] [%clk 0:00:23] }
13. b3?? { [%eval -4.14] [%clk 0:00:02] } 13... Nf4? { [%eval -2.73] [%clk 0:00:21] } 0-1
becomes
[Event "Rated Bullet tournament https://lichess.org/tournament/yc1WW2Ox"]
[Site "https://lichess.org/PpwPOZMq"]
[Date "2017.04.01"]
[Round "-"]
[White "Abbot"]
[Black "Costello"]
[Result "0-1"]
[UTCDate "2017.04.01"]
[UTCTime "11:32:01"]
[WhiteElo "2100"]
[BlackElo "2000"]
[WhiteRatingDiff "-4"]
[BlackRatingDiff "+1"]
[WhiteTitle "FM"]
[ECO "B30"]
[Opening "Sicilian Defense: Old Sicilian"]
[TimeControl "300+0"]
[Termination "Time forfeit"]
1. e4 c5 2. Nf3 Nc6 3. Bc4 e6 4. c3 b5 5. Bb3 c4 6. Bc2 a5 7. d4 cxd3 8. Qxd3 Nf6 9. e5 Nd5 10. Bg5 Qc7 11. Nbd2 h6 12. Bh4 Ba6 13. b3 Nf4 0-1
losing the comments
When you run the client (doesn't matter if compiled or downloaded) the client isn't working. It points to a fritzbox which denies access. When you execute it with ./client --hostname=http://lczero.org --user=foo --password=bar
it works.
Please consider changing the default address to lczero.org so it works out of the box.
so I'm trying to get some move ordering for my bot but I am not entirely sure how to check if a move is capture or a check.
There is a chess.Capture and a chess.Check obj and they say they require a movetag in order to work?
I was like okay how do I get a movetag and then I saw there is chess.Movetag() but IDK how that works
All I want to know if a move is capture or check
how do I check if a thing like this
game.MoveStr("e2e4")
is a check or a capture or neither?
Would love to have an easier implementation like a simple command is_check or is_capture done for this libary.
I did this first to go get the package:
go get -u github.com/notnil/chess
Then I run my start.go script and get this error:
go run -v start.go
start.go:6:2: no required module provides package github.com/notnil/chess: go.mod file not found in current directory or any parent directory; see 'go help modules'
go version
go version go1.16.2 linux/amd64
The hashing method it's very expensive (because of the MD5 dependency) to use it in any cache or transposition table. I suggest to implement Zobrist hashing wich is the de-facto standard.
Possible reference implementations:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.