This is my first C program. The code works, however I'm not yet familiar with how to write idiomatic C. How should I improve this?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define UNCLICKED 0
#define CLICKED 1
#define FLAGGED 2
#define NOT_BOMB 0
#define BOMB 1
struct piece {
char user_state, game_state, value;
};
int generate_rand();
int valid(int, int);
void print_board(struct piece[8][8], int);
int click(struct piece[8][8], int, int);
int game_over(struct piece[8][8]);
int main() {
struct piece board[8][8];
int i, j;
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
board[i][j].user_state = UNCLICKED;
board[i][j].game_state = NOT_BOMB;
board[i][j].value = 0;
}
}
srand(time(0));
int num_bombs = 10;
while (num_bombs > 0) {
int y, x;
do {
y = generate_rand();
x = generate_rand();
} while (board[y][x].game_state == BOMB);
board[y][x].game_state = BOMB;
int delta_row, delta_column;
for (delta_row = -1; delta_row <= 1; delta_row++) {
for (delta_column = -1; delta_column <= 1; delta_column++) {
if (valid(y + delta_row, x + delta_column)) {
board[y + delta_row][x + delta_column].value++;
}
}
}
num_bombs--;
}
print_board(board, 1);
while (1) {
char row, column, move_type;
printf("\nEnter a row: ");
row = getchar() - 48;
getchar();
printf("Enter a column: ");
column = getchar() - 48;
getchar();
if (!valid(row, column)) {
printf("Location is not on board. Try again: \n");
continue;
}
if (board[row][column].user_state == CLICKED) {
printf("That square is already open. Try again: \n");
continue;
}
printf("[C]lick or [F]lag? ");
move_type = getchar();
getchar();
printf("\n");
if (move_type == 'C') {
if (click(board, row, column)) {
printf("That was a bomb! \n");
print_board(board, 0);
break;
}
} else if (move_type == 'F') {
if (board[row][column].user_state == CLICKED) {
printf("That square is already open. Try again: \n");
continue;
} else {
board[row][column].user_state = FLAGGED;
}
} else {
printf("Enter either 'C' or 'F'. Try again: \n");
continue;
}
if (game_over(board)) {
printf("You win! \n");
print_board(board, 0);
break;
} else {
print_board(board, 1);
}
}
return 0;
}
int generate_rand() {
return (rand() % 8);
}
int valid(int row, int column) {
return row >= 0 && row < 8 && column >= 0 && column < 8;
}
void print_board(struct piece board[8][8], int in_game) {
int i, j;
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if (in_game) {
if (board[i][j].user_state == CLICKED) {
printf("%d ", board[i][j].value);
} else if (board[i][j].user_state == FLAGGED) {
printf("F ");
} else {
printf("- ");
}
} else {
if (board[i][j].game_state == NOT_BOMB) {
if (board[i][j].user_state == FLAGGED) {
printf("X ");
} else {
printf("%d ", board[i][j].value);
}
} else {
printf("B ");
}
}
}
printf("\n");
}
}
int click(struct piece board[8][8], int row, int column) {
if (!valid(row, column) || board[row][column].game_state == BOMB || board[row][column].user_state == CLICKED) {
return 1;
}
board[row][column].user_state = CLICKED;
if (board[row][column].value == 0) {
int i, j;
for (i = -1; i <= 1; i++) {
for (j = -1; j <= 1; j++) {
click(board, row + i, column + j);
}
}
}
return 0;
}
int game_over(struct piece board[8][8]) {
int i, j;
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if (board[i][j].game_state == NOT_BOMB && board[i][j].user_state == UNCLICKED) {
return 0;
}
}
}
return 1;
}
Would it make sense to put some of the code in a separate header file, or is that over complicating things?