bhlangonijr / chesslib

chess library for legal move generation, FEN/PGN parsing and more
Apache License 2.0
229 stars 80 forks source link

Get positions from pgn #11

Closed HBiSoft closed 5 years ago

HBiSoft commented 5 years ago

Hi, is it possible to get individual positions from a pgn file?

For example, when pressing "next" I would like to get the next move and when pressing "previous" I would like to get the previous move.

bhlangonijr commented 5 years ago

Hi @HBiSoft , Yes you can try the follow:

@Test
    public void testWalkingPgn() throws Exception {

        PgnHolder pgn = new PgnHolder("src/test/resources/oo.pgn");
        pgn.loadPgn();
        // iterate through the games or get the game you want to look at
        for (Game game: pgn.getGame()) {
            game.loadMoveText(); // lazily load the game you want to look at (in case we are just listing the games we don't need that)
            game.setCurrentMoveList(game.getHalfMoves()); // set the game list for "navigation"
            game.setBoard(new Board()); "set the board for associating the positions"
            game.gotoFirst(); // got to first position in the board
            System.out.println(game.getBoard().getFen()); 
            while (!game.isEndOfMoveList()) { // iterate till the end of the move list
                game.gotoNext(); // go to next position in the move list
                System.out.println(game.getBoard().getFen());  // print the FEN of the current position
                System.out.println(" Move (" + game.getPosition() + ") = " + game.getCurrentMoveList().get(game.getPosition()).getSan()  ); // print the current move in SAN format
                MoveList partialMoves = new MoveList();
                partialMoves.addAll(game.getCurrentMoveList().subList(0, game.getPosition() + 1));
                System.out.println(" Partial movelist: " + partialMoves.toSan()); // prints the current move list
            }
        }
    }

you also have the methods gotoPrior and gotoLast.

HBiSoft commented 5 years ago

Awsome, thank you very much.

HBiSoft commented 5 years ago

@bhlangonijr I only have one more issue, if you can please help me.

The problem I have is that partialMoves.toSan() will return, for example e4 e6. The next time I press Next it will return, for example e6 e4 d4 d5 and so on.

What I'm trying to achieve is to get the next move only for example if I press Next I want to get e6 then e4 and so on.

Is that possible?

This is what I currently have:

MoveList partialMoves = new MoveList();
partialMoves.addAll(refGame.getCurrentMoveList().subList(0, refGame.getPosition()));

String nextMove = partialMoves.toSan();

Each time I call the above the next 2 moves will be added to nextMove, instead of only giving me the next move only.

bhlangonijr commented 5 years ago

Hi @HBiSoft, The code below should behave exactly as you expect:

@Test
    public void testWalkingThorughPgn() throws Exception {
        PgnHolder pgn = new PgnHolder("src/test/resources/oo.pgn");
        pgn.loadPgn();
        for (Game game: pgn.getGame()) {
            game.loadMoveText();
            game.setCurrentMoveList(game.getHalfMoves());
            game.setBoard(new Board());
            game.gotoFirst();
            System.out.println(game.getBoard().getFen());
            System.out.println("First Move: " +  game.getCurrentMoveList().get(0).getSan());
            while (!game.isEndOfMoveList()) {
                game.gotoNext();
                MoveList partialMoves = new MoveList();
                partialMoves.addAll(game.getCurrentMoveList().subList(0, game.getPosition() ));
                System.out.println("Movelist: " + partialMoves.toSan() + " next Move: " +  game.getCurrentMoveList().get(game.getPosition()).getSan());

            }
        }
    }

For this PGN:

[Site "CCRL"]
[Date "2018.10.19"]
[Round "1"]
[White "CM10th Default"]
[Black "Fritz 9"]
[Result "1-0"]
[PlyCount "0"]
[TimeControl "?"]
[ECO "A13"]
[WhiteElo "2633"]
[BlackElo "2743"]

1. Nf3 d5 2. g3 e6 3. c4 d4 4. e3 Nc6 5. exd4 Nxd4 6. Bg2 Nh6 7. O-O Nhf5 8. d3 Be7 
9. Nxd4 Nxd4 10. Nd2 h5 11. Nb3 h4 12. Bd2 hxg3 13. hxg3 Bf6 14. Bc3 c6 15. Re1 Rh6 
16. Re4 Nf5 17. Bxf6 Qxf6 18. Qd2 Nd6 19. Rf4 Qe7 20. c5 Nf5 21. Re1 Qg5 22. Qa5 
Rh8 23. Re5 g6 24. Re1 Qd8 25. Qc3 Rh7 26. Rg4 Ne7 27. Ree4 Nd5 28. Qc1 Qf6 29. Rh4 
Rxh4 30. Rxh4 Bd7 31. Nd2 1-0

It'll give me:

First Move: Nf3
Movelist: Nf3  next Move: d5
Movelist: Nf3 d5  next Move: g3
Movelist: Nf3 d5 g3  next Move: e6
Movelist: Nf3 d5 g3 e6  next Move: c4
Movelist: Nf3 d5 g3 e6 c4  next Move: d4
Movelist: Nf3 d5 g3 e6 c4 d4  next Move: e3
Movelist: Nf3 d5 g3 e6 c4 d4 e3  next Move: Nc6
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6  next Move: exd4
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4  next Move: Nxd4
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4  next Move: Bg2
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2  next Move: Nh6
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6  next Move: O-O
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O  next Move: Nhf5
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O Nhf5  next Move: d3
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O Nhf5 d3  next Move: Be7
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O Nhf5 d3 Be7  next Move: Nxd4
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O Nhf5 d3 Be7 Nxd4  next Move: Nxd4
Movelist: Nf3 d5 g3 e6 c4 d4 e3 Nc6 exd4 Nxd4 Bg2 Nh6 O-O Nhf5 d3 Be7 Nxd4 Nxd4  next Move: Nd2
...

Maybe you somehow are calling gotoNext twice?

HBiSoft commented 5 years ago

@bhlangonijr Silly me, I was calling gotoNext twice.

" next Move: " + game.getCurrentMoveList().get(game.getPosition()).getSan()

also helped a lot.

Thank you very much for taking the time to answer.

bhlangonijr commented 5 years ago

@HBiSoft cool, let me know if I can help you in any issue

HBiSoft commented 5 years ago

@bhlangonijr I have one last issue, I promise this is the last one lol.

Currently I get the next position back as, for example fxe5 or Nc6, but when I make a move, it requires me to call the following:

board.doMove(new Move(Square.E2, Square.E4));

How would I then translate fxe5 to a square?

bhlangonijr commented 5 years ago

@HBiSoft The move list is stored in a long algebraic format consisting of the two full coordinates. What you have there fxe5 is the SAN format and depends on the current state of the chessboard to mean something. My point is you should try to store the long algebraic format for each move so that you can restore it in the chessboard easily at any point in time. Just don't use the move#toSan() for storing the move. move#toString should give you the long algebraic format. If you need the SAN notation again you can easily convert the move to it afterwards.

You can ask things whenever, I am not bothered because of that. :)

HBiSoft commented 5 years ago

@bhlangonijr Ok great that makes sense, so instead of getting the actual move that was played, I should get the "start" and "end" squares.

I did the following to get the above:

Game.getCurrentMoveList().get(refGame.getPosition()).toString()

This returns e4e5, I guess I can then convert that to uppercase and split the string to get 2 separate values, then set that to Move, like this:

String moveSquares = game.getCurrentMoveList().get(game.getPosition()).toString();
String startingSquare  = moveSquares.substring(0,2).toUpperCase(); //this returns E4
String endingSquare= moveSquares.substring(Math.max(moveSquares.length() - 2, 0)).toUpperCase(); //this returns E5

board.doMove(new Move(Square.fromValue(startingSquare), Square.fromValue(endingSquare)));
bhlangonijr commented 5 years ago

@HBiSoft I was not really sure of what you were trying to achieve. But if you just want to unmake one move you just call Move move = board.undoMove();. It'll retrieve the last move made and undo it. It can be called many times for as many moves that were made until you get to the start position.