pgnlib: a c++ library for PGN chess games

Tutorial - 7


7. The Position class

PGN format is based on minimal algebraic notation for moves. Our goal here is to take an input PGN game
and output it as a long algebraic notation (non-PGN) game.

In other words we start with a something like "1.e4 e5 2.Nf3 Nc6 3..." and we want to end up with
"1.e2-e4 e7-e5 2.Ng1-f3 Nb8-c6 3...".

This is the game we have to deal with:

int main(int argc, char *argv[])
{
        try
        {
                // this time we just hard-coded a single game, instead of reading it from file.
                std::string pgn_game =
                          "[Event \"Olympiad w\"]\n"
                          "[Site \"Bled SLO\"]\n"
                          "[Date \"2002.10.26\"]\n"
                          "[Round \"1\"]\n"
                          "[White \"Dimovski,A\"]\n"
                          "[Black \"Zhao Xue\"]\n"
                          "[Result \"0-1\"]\n"
                          "[WhiteElo \"2100\"]\n"
                          "[BlackElo \"2367\"]\n"
                          "[ECO \"B90\"]\n"
                          "\n"
                          "1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 a6 6. Nb3 e6 7. Bd3 Be7 8.\n"
                          "O-O Nbd7 9. f4 b5 10. Qe2 Qc7 11. Bd2 Bb7 12. Nd1 Nc5 13. Nf2 d5 14. e5\n"
                          "Nfe4 15. Ba5 Qc8 16. Nd4 O-O 17. Ng4 Kh8 18. Rf3 f5 19. exf6 gxf6 20. Rh3\n"
                          "Nxd3 21. Qxd3 Bc5 22. Ne3 e5 23. fxe5 fxe5 24. Nf3 Qxh3 25. gxh3 Rxf3 0-1\n\n";

                // put it into the game object in the usual way.
                pgn::Game the_game;
                std::stringstream ss;
                ss << pgn_game;
                ss >> the_game;
		...
}

As we would like to print out the game in a non PGN fashion we could start with a game header, keeping
things as simple as possible.

int main(int argc, char *argv[])
{
	...
        // write down the just the players' name 
        game_header_printout(the_game);
	...
}

// print a minimal and ugly game header
void game_header_printout(const pgn::Game &game)
{
        // get infos from pgn tag list ...
        pgn::TagList tags = game.tags();

        std::string white_player = tags["White"].value();
        std::string black_player = tags["Black"].value();

        // ... and print them out
        std::cout << std::endl << "------------- " << white_player << " - " << black_player << " -------------" << std::endl;
}

Now we have to convert and print the moves; that's not as straightforward as it may seem because we have
very little information about the starting square of a piece on the move in the pgn text.

We need to know where each piece stands on the board before the move is actually made: this is where
the Position class comes in handy.

int main(int argc, char *argv[])
{
	...
        // As usual we get the moves from the game first
        pgn::MoveList movelist = the_game.moves();
        // and then we define a Position object, by default initialized with the initial piece arrangement
        pgn::Position p;
	...
}

We just feed the moves to the Position object, calling the method Position::update().
This function actually updates the position on the board and, as a side effect, fills up the Ply object
received from the caller.

int main(int argc, char *argv[])
{
	...
        for (pgn::MoveList::iterator itr = movelist.begin(); itr != movelist.end(); itr++)
        {
	     // print the move number on stdout.
             std::cout << p.moveNumber() << ".";

             // get the white ply, feed it to the position, get it back completed and print it.
             pgn::Ply ply;
             ply = itr->white();
             // update the position and get the ply filled up in exchange.
             p.update(ply);
             // move_printout converts ply from pgn to algebraic and print it out
             move_printout(ply);

             // same stuff for black move
             ply = itr->black();
             if (!ply.valid()) break; // the game could possibly end with a white move (not this game).
             p.update(ply);
             move_printout(ply);

             // six moves for every line, please
             if ((p.moveNumber()-1) % 6 == 0)
                  std::cout << std::endl;
        }
	...
}

The move_printout routine is in charge of doing the format conversion, that's not difficult.

void move_printout(const pgn::Ply &ply)
{
        // castling output is the same in pgn or algebraic.
        if (ply.isLongCastle() || ply.isShortCastle())
                std::cout << ply << " ";
        else // normal move conversion
                std::cout << ply.piece() << ply.fromSquare()
                          << (ply.isCapture() ? "x" : "-")
                          << ply.toSquare()
                          << (ply.isCheckMate() ? "#" : " ");
}

Now we print the game outcome and we are done.

int main(int argc, char *argv[])
{
	...
        // so, who won?
        std::cout << the_game.result() << std::endl << std::endl;
}

And this is the output of the program:


------------- Dimovski,A - Zhao Xue -------------

1.e2-e4 c7-c5 2.Ng1-f3 d7-d6 3.d2-d4 c5xd4 4.Nf3xd4 Ng8-f6 5.Nb1-c3 a7-a6 6.Nd4-b3 e7-e6 
7.Bf1-d3 Bf8-e7 8.O-O Nb8-d7 9.f2-f4 b7-b5 10.Qd1-e2 Qd8-c7 11.Bc1-d2 Bc8-b7 12.Nc3-d1 Nd7-c5 
13.Nd1-f2 d6-d5 14.e4-e5 Nf6-e4 15.Bd2-a5 Qc7-c8 16.Nb3-d4 O-O 17.Nf2-g4 Kg8-h8 18.Rf1-f3 f7-f5 
19.e5xf6 g7xf6 20.Rf3-h3 Nc5xd3 21.Qe2xd3 Be7-c5 22.Ng4-e3 e6-e5 23.f4xe5 f6xe5 24.Nd4-f3 Qc8xh3 
25.g2xh3 Rf8xf3 0-1


You can find the complete code for all these examples from this tutorial (and a couple more)
into the distribution tarball.

[6. Moves and pieces] [home page] [8. A simple xboard extension]

SourceForge.net Logo Valid HTML 4.01!