#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
void reset_game(char board[3][3], bool* player);
bool save(char board[3][3], bool player);
bool load(char board[3][3], bool* player);
int check_win(char board[3][3]);
void print_board(char board[3][3]);
int get_int_from_user(const char* prompt, ...);
bool get_YN_from_user(const char* prompt, ...);
int main()
{
char board[3][3];
bool first_player;
int choice;
reset_game(board, &first_player);
for (;;)
{
print_board(board);
choice = get_int_from_user(
"Enter a number 1 to 9 to select a field, or\n"
"the number 10 to save the game, or\n"
"the number 11 to load the game, or\n"
"the number 12 to quit the game.\n"
"Player %d, enter your choice :\n",
first_player ? 1 : 2
);
switch (choice)
{
case 10:
if (save(board, first_player))
{
printf("Save successful.\n");
}
else
{
printf("Save failed!\n");
}
break;
case 11:
if (load(board, &first_player))
{
printf("Load successful.\n");
}
else
{
printf("Load failed!\n");
printf("Resetting game, as the game may now be in a corrupt state.\n");
reset_game(board, &first_player);
}
break;
case 12:
printf("Bye!\n");
exit(EXIT_SUCCESS);
default:
if (1 <= choice && choice <= 9)
{
int i;
i = choice - 1;
int row = i / 3;
int column = i % 3;
if (board[row][column] != 'E')
{
printf("Invalid choice! The specified field is not empty!\n");
break;
}
board[row][column] = first_player ? 'X' : 'O';
i = check_win(board);
if (i != -1)
{
print_board(board);
if (i == 1)
printf("==>Player %d won\n", first_player ? 1 : 2);
else
printf("==>Game draw\n");
if (get_YN_from_user("Do you want to play again? (y/n)\n"))
{
reset_game(board, &first_player);
continue;
}
else
exit(EXIT_SUCCESS);
}
first_player = !first_player;
}
else
{
printf("Invalid choice! Choice must be a number between 1 and 12.\n");
}
}
}
}
void reset_game(char board[3][3], bool* first_player)
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
board[i][j] = 'E';
*first_player = true;
}
int check_win(char board[3][3])
{
for (int i = 0; i < 3; i++)
{
char c = board[i][0];
if (c != 'E' && board[i][1] == c && board[i][2] == c)
return 1;
}
for (int i = 0; i < 3; i++)
{
char c = board[0][i];
if (c != 'E' && board[1][i] == c && board[2][i] == c)
return 1;
}
for (int i = 0; i < 3; i++)
{
char c;
c = board[0][0];
if (c != 'E' && board[1][1] == c && board[2][2] == c)
return 1;
c = board[2][0];
if (c != 'E' && board[1][1] == c && board[0][2] == c)
return 1;
}
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i][j] == 'E')
return -1;
return 0;
}
void print_board(char board[3][3])
{
printf("\n\n Tic Tac Toe \n\n");
printf("Player1 (X) - Player2 (O) \n\n\n");
for (int i = 0; i < 3; i++)
{
printf(" | | \n");
printf(
" %c | %c | %c \n",
board[i][0] == 'E' ? '1' + i * 3 : board[i][0],
board[i][1] == 'E' ? '2' + i * 3 : board[i][1],
board[i][2] == 'E' ? '3' + i * 3 : board[i][2]
);
if (i != 2)
printf(" _____|_____|_____\n");
else
printf(" | | \n");
}
printf("\n");
}
bool save(char board[3][3], bool first_player)
{
FILE* fp;
fp = fopen("savefile.txt", "wt");
if (fp == NULL)
{
fprintf(stderr, "Error opening file\n");
return false;
}
if (fputc(first_player ? '1' : '2', fp) == EOF)
{
fprintf(stderr, "Error writing to file\n");
fclose(fp);
return false;
}
if (fwrite(board, 9, 1, fp) != 1)
{
fprintf(stderr, "Error writing to file\n");
fclose(fp);
return false;
}
fclose(fp);
return true;
}
bool load(char board[3][3], bool* first_player)
{
FILE* fp;
fp = fopen("savefile.txt", "rt");
if (fp == NULL)
{
fprintf(stderr, "Error opening file\n");
return false;
}
switch (fgetc(fp))
{
case EOF:
fprintf(stderr, "Error reading from file\n");
fclose(fp);
return false;
break;
case '1':
*first_player = true;
break;
case '2':
*first_player = false;
break;
default:
fprintf(stderr, "Unexpected character found in file!\n");
fclose(fp);
return false;
}
if (fread(board, 9, 1, fp) != 1)
{
fprintf(stderr, "Error reading from file\n");
fclose(fp);
return false;
}
fclose(fp);
return true;
}
int get_int_from_user(const char* prompt, ...)
{
for (;;)
{
char buffer[1024], * p;
va_list vl;
long l;
va_start(vl, prompt);
vprintf(prompt, vl);
va_end(vl);
if (fgets(buffer, sizeof buffer, stdin) == NULL)
{
fprintf(stderr, "unrecoverable error reading from input\n");
exit(EXIT_FAILURE);
}
if (strchr(buffer, '\n') == NULL)
{
int c;
printf("line input was too long!\n");
do
{
c = getchar();
if (c == EOF)
{
fprintf(stderr, "unrecoverable error reading from input\n");
exit(EXIT_FAILURE);
}
} while (c != '\n');
continue;
}
errno = 0;
l = strtol(buffer, &p, 10);
if (p == buffer)
{
printf("error converting string to number\n");
continue;
}
if (errno == ERANGE || l < INT_MIN || l > INT_MAX)
{
printf("number out of range error\n");
continue;
}
for (; *p != '\0'; p++)
{
if (!isspace((unsigned char)*p))
{
printf("unexpected input encountered!\n");
goto next_outer_loop_iteration;
}
}
return l;
next_outer_loop_iteration:
continue;
}
}
bool get_YN_from_user(const char* prompt, ...)
{
for (;;)
{
char buffer[1024];
va_list vl;
va_start(vl, prompt);
vprintf(prompt, vl);
va_end(vl);
if (fgets(buffer, sizeof buffer, stdin) == NULL)
{
fprintf(stderr, "unrecoverable error reading from input\n");
exit(EXIT_FAILURE);
}
if (strchr(buffer, '\n') == NULL)
{
int c;
printf("line input was too long!\n");
do
{
c = getchar();
if (c == EOF)
{
fprintf(stderr, "unrecoverable error reading from input\n");
exit(EXIT_FAILURE);
}
} while (c != '\n');
continue;
}
return toupper((unsigned char)buffer[0]) == 'Y';
}
}