Theft again; alas.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/*-- Constants --*/
/// How many pieces we're trying to connect
#define CONNECT 4
/// The minimum and maximum board dimensions
#define MIN_BOARD_DIMENSION 4
#define MAX_BOARD_WIDTH 9
#define MAX_BOARD_HEIGHT 16
/// The three cell types
#define CELL_EMPTY '.'
#define CELL_RED 'R'
#define CELL_YELLOW 'Y'
/// The winner conditions
#define WINNER_NONE 0
#define WINNER_RED 1
#define WINNER_YELLOW 2
/// Whose turn is it?
#define TURN_RED 0
#define TURN_YELLOW 1
/*-- Function prototypes --*/
void assert_board_dimension(int dimension, int min, int max);
void initialise_board(void);
void play_game(void);
int play_turn(int whose_turn);
int check_winner(void);
int check_line(int start_row, int start_col, int offset_row, int offset_col);
// provided for you:
bool is_board_full(void);
void print_board(void);
/*-- Global variables --*/
char board[MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH];
int board_width;
int board_height;
/*-- Code --*/
int main(void) {
printf("Enter board width: ");
scanf("%d", &board_width);
assert_board_dimension(board_width, MIN_BOARD_DIMENSION, MAX_BOARD_WIDTH);
printf("Enter board height: ");
scanf("%d", &board_height);
assert_board_dimension(board_height, MIN_BOARD_DIMENSION, MAX_BOARD_HEIGHT);
initialise_board();
print_board();
play_game();
return 0;
}
/// Make sure the board dimensions we're
/// given fit within the correct bounds
void assert_board_dimension(int dimension, int min, int max) {
if (dimension < min) {
printf("Board dimension too small (min %d)\n", min);
exit(1);
}
if (dimension > max) {
printf("Board dimension too large (max %d)\n", max);
exit(1);
}
}
/// Initialise the board to all empty cells
void initialise_board(void) {
for (int row = 0; row < board_height; row++) {
for (int col = 0; col < board_width; col++) {
board[row][col] = CELL_EMPTY;
}
}
}
/// The main game loop
void play_game(void) {
int whose_turn = TURN_RED;
int winner;
// Play a single turn
do {
whose_turn = play_turn(whose_turn);
print_board();
winner = check_winner();
} while (winner == WINNER_NONE && !is_board_full());
// Either we have a winner, or the board is full!
if (winner == WINNER_NONE) {
printf("The game is a draw!\n");
} else if (winner == WINNER_RED) {
printf("Game over, Red wins!\n");
} else {
printf("Game over, Yellow wins!\n");
}
}
/// Play a single turn!
/// This in sequence:
/// - Reads in the column to insert,
/// - Makes sure it is valid,
/// - Inserts the color into that column,
/// - Switches the turn to the next player.
int play_turn(int whose_turn) {
if (whose_turn == TURN_RED) printf("[RED] ");
else printf("[YELLOW] ");
printf("Choose a column: ");
int target_col = 0;
scanf("%d", &target_col);
target_col--; // user input is 1-indexed
if (target_col < 0 || target_col >= board_width) {
printf("Invalid column\n");
return whose_turn;
}
int target_row = board_height - 1;
while (target_row >= 0 && board[target_row][target_col] != CELL_EMPTY) {
target_row--;
if (target_row < 0) {
printf("No space in that column!\n");
return whose_turn;
}
}
if (whose_turn == TURN_RED) {
board[target_row][target_col] = CELL_RED;
return TURN_YELLOW;
} else {
board[target_row][target_col] = CELL_YELLOW;
return TURN_RED;
}
}
/// Checks if the game has a winner yet!
/// For each position on the board, the
/// loop will check vertical, horizontal,
/// North-East vertical, and
/// North-West vertical for a connection.
int check_winner(void) {
for (int row = 0; row < board_height; row++) {
for (int col = 0; col < board_width; col++) {
int check;
check = check_line(row, col, 1, 0);
if (check != WINNER_NONE) return check;
check = check_line(row, col, 0, 1);
if (check != WINNER_NONE) return check;
check = check_line(row, col, 1, 1);
if (check != WINNER_NONE) return check;
check = check_line(row, col, 1, -1);
if (check != WINNER_NONE) return check;
}
}
return WINNER_NONE;
}
/// Checks if a particular line represents
/// a connect 4!
/// It uses a start_row and start_col,
/// and then checks if there are 3 further
/// tokens of the same color, offsetting by
/// offset_row and offset_col each time.
int check_line(int start_row, int start_col, int offset_row, int offset_col) {
//printf("Debug1: \n%d\n%d\n%d\n%d\n", start_row, start_col, offset_row, offset_col);
char first_cell = board[start_row][start_col];
if (first_cell == CELL_EMPTY) return WINNER_NONE;
int row = start_row + offset_row;
int col = start_col + offset_col;
for (int i = 0; i < CONNECT - 1; i++) {
if (row < 0 || col < 0) return WINNER_NONE;
if (row >= board_height || col >= board_width) return WINNER_NONE;
char cell = board[row][col];
if (cell != first_cell) return WINNER_NONE;
row += offset_row;
col += offset_col;
}
if (first_cell == CELL_RED) return WINNER_RED;
else return WINNER_YELLOW;
}
/// Checks if the board is completely full
/// i.e. there is no free space for a turn.
/// This is the condition that causes a draw
bool is_board_full(void) {
for (int row = 0; row < board_height; row++) {
for (int col = 0; col < board_width; col++) {
if (board[row][col] == CELL_EMPTY) return false;
}
}
return true;
}
/// Print the board out to the terminal,
/// with numbers at the top to indicate
/// the column indicies.
void print_board(void) {
printf("\n");
for (int col = 0; col < board_width; col++) {
printf("%d ", col + 1);
}
printf("\n");
for (int row = 0; row < board_height; row++) {
for (int col = 0; col < board_width; col++) {
printf("%c ", board[row][col]);
}
printf("\n");
}
}