To do what you want, you'll need a move validator. To make things clear, let's take a look at the Stockfish iOS chess playing app. The app uses the Stockfish chess engine. The outputs from the engine would be something like:
e2e4 e7e5 ...
But that's not what we want to see. We'd need to convert the outputs to something like:
1. e4 e5
How do we do it? The open-source Stockfish app has a function called move_to_san defined. Please download the full source code from the Stockfish web site. Here, we'll briefly go through the implementation.
const std::string move_to_san(Position &pos, Move m)
This is the header of the function. It takes a position object and a move. That's why I asked you whether you have a move validator implemented. You'll need a validator for this position object as we will see.
from = move_from(m);
to = move_to(m);
pc = pos.piece_on(move_from(m));
Here, we grab the source and destination square. We also need the moving piece.
else if(move_is_long_castle(m)
|| (int(to - from) == -2 && type_of_piece(pc) == KING))
str = "O-O-O";
else if(move_is_short_castle(m)
|| (int(to - from) == 2 && type_of_piece(pc) == KING))
str = "O-O";
Next, we check if the move is a castle move. Note that it's a good idea to store the move as "e1 to h1". This is not what we normally expect, we always do move the e1 king to the g1 square then the h1 rook to the f1 square. The conditions in the code take care of that.
str = "";
if(type_of_piece(pc) == PAWN) {
if(pos.move_is_capture(m))
str += file_to_char(square_file(move_from(m)));
}
This is starting to get interesting. We build an empty buffer. Once we've done it, we'd need to ask if this is a pawn move. If it's a pawn move, convert it to an ASCI character. If our move is e2e4, the buffer would have been "e" by now.
str += piece_type_to_char(type_of_piece(pc), true);
If this is not a pawn move, we convert the piece to a character. This can be done by a dictionary if you prefer.
if(pos.move_is_capture(m))
str += "x";
str += square_to_string(move_to(m));
Next, if it's a capture, we add an "x" to it. Then, we'll need to add the destination square.
if(move_promotion(m)) {
str += "=";
str += piece_type_to_char(move_promotion(m), true);
}
Promotion.
pos.do_move(m, u);
if(pos.is_check())
str += pos.is_mate()? "#" : "+";
pos.undo_move(m, u);
Is this a check?
What have we learnt so far?
- We need an internal board representation. For example, the code pos.do_move(m, u) needs a mechanism to save the board. You can implement a bitboard, a mailbox or an object-oriented design.
- We need a way to tell where the move comes from, where it goes to etc. This can be done by simple logical operations.
Please download the Stockfish iOS source code. Study the code. You can post a new question if you're not sure what to do with the code and I'll help you.
Everything here is in C++. You might need to port the code to Ruby.