1.
#define SPACE ' '
- смысла не несет такая замена. Или какое-то более осмысленное имя, вроде EMPTY_FIELD, или просто в коде используйте ' '. Хотя лучше хорошее имя.
2. check_field - ужас. Во-первых, проверки на крестики и на нолики идентичны - только сравниваете вы или с 'X' или с 'O'. Ну напишите функцию, которая принимает char и ищет тройку из этих символов. Далее, используйте циклы. 3 строки можно проверить одним и тем же кодом, только индексы сдвигаются на 3 - вот и запустите цикл на 3 итерации. Так же со столбцами. В итоге вместо 8 условий будет только 4.
Вообще, можно до одного условия в трех вложенных циклах ужать:
const int dx[4] = {0, 1, 1, 1};
const int dy[4] = {1, 0, 1, -1};
for (dir = 0; dir < 4; ++dir) {
for (int start = 0; start < 9; ++start) {
int step;
int x = start % 3;
int y = start / 3;
for (step = 0; step < 3; ++step) {
if (x < 0 || x > 2 || y < 0 || y > 3 || field[x+3*y] != player) break;
x += dx[dir];
y += dy[dir];
}
if (step == 3) {
return 1;
}
}
}
return 0;
3. print_field тоже делается циклами. Хотя бы по трем строкам.
4. основной игровой цикл можно делать while (1), ибо по любому из значений check происходит break;