So I am reading a .txt file that has many sets of chess moves. I am able to read data from the file and insert the line into a string.
An example single chess move can look like this:
1. e4 e5
I have written the following function to parse a single chess move:
void parseSingleChessMove(string move)
{
this->moveNumber = stoi(move.substr(0, move.find(".")));
this->move[0] = move.substr(move.find_first_of(" ") 1, move.find_last_of(" ")-move.find_first_of(" ")-1);
this->move[1] = move.substr(move.find_last_of(" ") 1);
}
I am parsing the string and storing it within a self-defined Move Class hence the use of the 'this' operator. This function works perfectly and stores each field of a single chess move. move[0] stores the first move and move[1] stores the second move, while the moveNumber data member stores the number the move was played at.
I am creating an array of the Move Class in order to store every single move of a chess match in order. However, a complete set of chess moves can look something like this:
1. Nf3 Nf6 2. c4 c6 3. g3 g6 4. b3 Bg7 5. Bb2 O-O 6. Bg2 d5 7. O-O Bf5 8. d3
Nbd7 9. Nd4 e6 10. h3 h5
I am having a hard time trying to figure out how to store each of these individual moves in the array of Move Class from a string of a set of chess moves.
The main issue is reading the string only until a move number is found. I then need to obtain a substring for a move (something like 4. b3 Bg7
and then parsing this single chess move using the above function so that I can store moveNumber=4, move[0]="b3" and move[1]="Bg7" and finally storing it into the respective index of array type Move Class. And then repeating this until all moves are stored one by one and we reach the end of the string.
EDIT: This is my class definition:
class MoveNode {
public:
array<string, 2> move;
int moveNumber;
void parseSingleChessMove(string move)
{
this->moveNumber = stoi(move.substr(0, move.find(".")));
this->move[0] = move.substr(move.find_first_of(" ") 1, move.find_last_of(" ")-move.find_first_of(" ")-1);
this->move[1] = move.substr(move.find_last_of(" ") 1);
}
}
I am storing all of the moves in this array:
MoveNode *setofMoves = new MoveNode[totalMoves];
CodePudding user response:
You could use regular expressions for this:
- The pattern to search repeatedly for would be:
(\d )\.
a number of one or more digits (which we are going to capture, hence the use of parentheses), followed by a dot; then\s ([^\s] )
one or more whitespaces, followed by one or more non-whitespaces (and we capture these latter); we repeat this pattern twice, once for each move; finally(:?\s |$)
, one or more whitespaces\s
or|
the end of the expression$
because the input line may end with a second move (and we don't capture this group(:?)
).
We usestd::regex
to store the pattern, wrapping it all withinR"()"
, so that we can write the raw expression. - The
while
loop does a few things: it searches the next match withregex_search
, extracts the captured groups (move number, move 0 and move 1), and updates the input line, so that the next search will start where the current one finished.matches
is an array whose first element,matches[0]
, is the part ofline
matching the whole pattern, and the next elements correspond to the pattern's captured groups.
#include <iostream> // cout
#include <regex> // regex_search, smatch
int main() {
std::string line{"1. Nf3 Nf6 2. c4 c6 3. g3 g6 4. b3 Bg7 5. Bb2 O-O 6. Bg2 d5 7. O-O Bf5 8. d3 Nbd7 9. Nd4 e6 10. h3 h5"};
std::regex pattern{R"((\d )\.\s ([^\s] )\s ([^\s] )(:?\s |$))"};
std::smatch matches{};
while (std::regex_search(line, matches, pattern))
{
std::cout
<< "moveNum=" << matches[1] << ", "
<< "move[0]=" << matches[2] << ", "
<< "move[1]=" << matches[3] << "\n";
line = matches.suffix();
}
}
// Outputs:
// moveNum=1, move[0]=Nf3, move[1]=Nf6
// moveNum=2, move[0]=c4, move[1]=c6
// moveNum=3, move[0]=g3, move[1]=g6
// moveNum=4, move[0]=b3, move[1]=Bg7
// moveNum=5, move[0]=Bb2, move[1]=O-O
// moveNum=6, move[0]=Bg2, move[1]=d5
// moveNum=7, move[0]=O-O, move[1]=Bf5
// moveNum=8, move[0]=d3, move[1]=Nbd7
// moveNum=9, move[0]=Nd4, move[1]=e6
// moveNum=10, move[0]=h3, move[1]=h5
CodePudding user response:
@rturrado showcased how you could do this with regex, however I would hesitate to do it like that, since std::regex
is heavy, and requires a lot of knowledge regarding regex to use it effectively. Instead, I think it's easier to accomplish it with istream
and operator>>
.
void parse_moves(std::istream& input)
{
int move_number;
char dot;
std::string move_fisrt, move_second;
int index = 0;
while(input >> move_number >> dot >> move_first >> move_second)
{
setofMoves[index] = MoveNode{{move_first, move_second}, move_number};
index;
}
}
Here while(is >> ...)
will keep parsing the text as long it's following the pattern.