I am currently working on a project for one of my first programming classes. We are building a C program that reads a .txt file, which contains information about an Othello (aka Reversi) match, and determines whether the match is correct or not (if it followed the rules, the moves were correct, etc.). The style of the .txt received is the following:
FirstName,Letter1
FirstName,Letter2
Letter
Plays
Where Letter1 and Letter2 are either B or N (for white and black in spanish, respectively), then the following line is either B or N again, which determines who starts moving (in our version of Othello both colours may start) and then plays is a series of positions on the 8x8 grid, represented by , with letters from A to H and numbers from 1 to 8. There is a single play per line, so an example of an (incomplete) match could be
John,N
Martin,B
N
F5
D6
C3
Where the first move is John who plays black F5.
My problem arises with the possibility of a player skipping a turn. Players may (and must) skip their turn if and only if there is no possible play to be made. So, if a player skips a turn, an empty line will be in place of their play. Therefore, there might be a .txt file with the following style (this is an impossible move, just in case)
F5
D6
C3
Where D6 and C3 were both plays made by the same player, consecutively, since their opponent skipped their turn.
So, in a certain part of my program, I have a read_file function that goes through the .txt file and saves the information about the game on a struct I called "datos". In particular, one of its fields is plays, which is an array of strings. There, I am storing the strings of the format for normal plays and "XX" for skipped turns. So the array for the last game would be ["F5", "D6", "XX", "C3"].
I have spents 2 days working on this function and when it finally seemed to work I realized it was not saving the skipped turns at all. Instead, when a player skipped its turn, the function stopped saving the plays made and moved on with the program, leaving the match incomplete. This is my current code, it does not even try to store the "XX" plays because I have tried a thousand methods and they always fail.
char play[3];
while (fscanf(file, "%s\n", play) == 1) {
if (strlen(play) != 2 || jugada[0] < 'A' || jugada[0] > 'H' || jugada[1] < '1' || jugada[1] > '8') {
fclose(file);
free_datos(match);
return 5;
}
//if (jugada[0] == '\n' && jugada[1] == '\0') strcpy(jugada, "XX");
match->plays_counter++;
match->plays = realloc(match->plays, match->plays_counter * sizeof(char *));
match->plays[match->plays_counter - 1] = malloc(MAX_PLAY * sizeof(char));
strcpy(match->plays[match->plays_counter - 1], play);
}
This current version just ends the while loop when it finds an empty line, which is simply not what I need it to do.
I tried multiple versions, using fscanf, fgets, chatGPT, searched stackOverflow and found no answers to my problem. I have read suggested posts but most deal with the removal of empty lines rather than actually wanting to take them into account.
About the other versions, sometimes they would add a "XX" after every play, doubling the size of the array (but correctly recognizing skipped turns, lol), more often than not they would just crash or end the while loop after finding a skipped turn, etc. I have asked chatGPT too many times and it keeps providing me with wrong answers.
Could someone help me out? I have tried with fgets but all of the versions I tried would also not work. For some reason I could not find any more info on youtube or anywhere, most of the times what to do when working with empty lines was overlooked.
Sorry if this was too long, I have been working on this for a surprisingly large amount of time considering it should really not be that hard. It is driving me crazy at this point. Thank you in advance!
Your problem is that the function call
does not do what you want.
The
%s
specifier will first consume and discard all whitespace characters (which includes spaces and newline characters), and then when it encounters a non-whitespace character, it will read everything it encounters until the next whitespace character.The
\n
character in the format string will instructscanf
to consume and discard all whitespace characters (not just a single one!), until it encounters a non-whitespace character.This means that with the input
the first call to
fscanf
will readF5\n
, the second call tofscanf
will readD6\n\n
and the third function call will readC3\n
. However, this is not what you want. You want the second call to only read one line instead of two lines, i.e. you want it to read onlyD6\n
instead ofD6\n\n
.I do not recommend that you attempt to solve this problem with
scanf
. Instead, I suggest that you usefgets
, as that function behaves in a more intuitive manner. In particular, that function will always read a single line at once (unless the supplied memory buffer is not large enough to store the entire line).Here is an example program:
For the input
this program has the following output: