An assignment this past semester was to write functions such that the following C program would run:
int main() { int board[BOARD_SIZE_HORIZ][BOARD_SIZE_VERT] = { {0} }; int player_num, computer_num; int last_move; /* Ask Alice if she wants to go first */ player_num = print_welcome(); if (player_num == 1) computer_num = 2; else computer_num = 1; /* If Alice wants to go first, let her make a move */ if (player_num == 1) { display_board(board); last_move = player_move(board,player_num); display_board(board); } while (1) { /* Make a computer move, then display the board */ last_move = best_move(board,computer_num); printf("Computer moved in column: %d\n", last_move); display_board(board); /* Check whether the computer has won */ if (check_win_or_tie(board,last_move)) return 0; /* Let Alice make a move, then display the board */ last_move = player_move(board,player_num); display_board(board); /* Check whether Alice has won */ if (check_win_or_tie(board,last_move)) return 0; } }
Writing functions so the computer would play against the player was easy, but the computer just made a random move (obviously too easy). For extra credit, we could write a function (+sub-functions as needed) to have the computer make the 'best' move in a situation. I put some work into this, and my 'AI' would make fairly good moves in many situations, but my code came in 17th of 24 when played against the other extra-credit programs.
The full code is quite long- I'll post it at the very end so you can see everything
My approach in the best_move function was: First, check whether there are any moves that would win the game, and make the move (either to win or block the player from winning):
int check3win(int board[][BOARD_SIZE_VERT], int computer_num)
{
int winMove;
int blockMove;
int playerNum;
playerNum = (computer_num == 1 ? 2 : 1);
if (check3_vertwin(board, computer_num) != 8) {
winMove = check3_vertwin(board, computer_num);
return winMove;
}
if (check3_horizwin(board, computer_num) != 8) {
winMove = check3_horizwin(board, computer_num);
return winMove;
}
if (check3_diagRUwin(board, computer_num) != 8) {
winMove = check3_diagRUwin(board, computer_num);
return winMove;
}
if (check3_diagRDwin(board, computer_num) != 8) {
winMove = check3_diagRDwin(board, computer_num);
return winMove;
}
if (check3_vertwin(board, playerNum) != 8) {
blockMove = check3_vertwin(board, playerNum);
return blockMove;
}
if (check3_horizwin(board, playerNum) != 8) {
blockMove = check3_horizwin(board, playerNum);
return blockMove;
}
if (check3_diagRUwin(board, playerNum) != 8) {
blockMove = check3_diagRUwin(board, playerNum);
return blockMove;
}
if (check3_diagRDwin(board, playerNum) != 8) {
blockMove = check3_diagRDwin(board, playerNum);
return blockMove;
}
return 8;
}
(The check3_***** functions each check whether there are 3 in a row anywhere in the board, for the four types of wins - horizontal, vertical diagonal Right-Down, diagonal Right-Up)
If not, the computer will use 'build2', which checks whether there are moves to make that would create 3 in a row:
int build2(int board[][BOARD_SIZE_VERT], int computer_num)
{
int build2;
if (build2_diagDN(board,computer_num) != 8)
{
build2 = build2_diagDN(board,computer_num);
return build2;
}
if (build2_diagUP(board,computer_num) != 8)
{
build2 = build2_diagUP(board,computer_num);
return build2;
}
if (build2vert(board,computer_num) != 8)
{
build2 = build2vert(board,computer_num);
return build2;
}
if (build2horiz(board,computer_num) != 8)
{
build2 = build2horiz(board,computer_num);
return build2;
}
return 8;
}
The functions I have written do what they're supposed to do, but I quickly realized there's gotta be a more straightforward way to have the program make a good move, not just conditionals upon conditionals checking for wins, blocks, trying to build 3 in a row, trying to build 2 in a row...
I'd like to see if there are better ways to go about this.
Note(s): The other 'similar' questions I could find were asking about programs for player-versus-player versions, i.e. no 'AI' portion at all, or were for different languages that I don't know.
Here is the full code for each of the three files -
Functions file c4_functions.c (this is what I had to write)
#include "connect4_functions.h"
#include <ctype.h>
#include <sys/types.h>
/* Forward function declarations */
bool check_diagRD (int board[][BOARD_SIZE_VERT], int m);
bool check_diagRU (int board[][BOARD_SIZE_VERT], int m);
bool check_vertical (int board[][BOARD_SIZE_VERT], int m);
bool check_horizontal (int board[][BOARD_SIZE_VERT], int m);
int build2 (int board[][BOARD_SIZE_VERT], int computer_num);
int build2_diagDN (int board[][BOARD_SIZE_VERT], int computer_num);
int build2_diagUP (int board[][BOARD_SIZE_VERT], int computer_num);
int build2horiz (int board[][BOARD_SIZE_VERT], int computer_num);
int build2vert (int board[][BOARD_SIZE_VERT], int computer_num);
int check3win (int board[][BOARD_SIZE_VERT], int computer_num);
int check3_diagRDwin (int board[][BOARD_SIZE_VERT], int computer_num);
int check3_diagRUwin (int board[][BOARD_SIZE_VERT], int computer_num);
int check3_horizwin (int board[][BOARD_SIZE_VERT], int computer_num);
int check3_vertwin (int board[][BOARD_SIZE_VERT], int computer_num);
/* Function print_welcome() */
int print_welcome(void)
{
char c;
/* Seed the rand() function to be called from random_move.
Since connect4.c can't be changed & doesn't include a seed,
it's included here, as this function is only called once
and prior to all calls to random_move (and thus to rand() */
srand(time(NULL));
printf("\n*** Welcome to the Connect-Four game!!! ***\n");
printf("Would you like to make the first move [y/n]: \n");
c = getchar();
while (getchar() != '\n') { } //clears stdin
if (c == 'n' || c == 'N') return 2;
else return 1;
}
/* Function display_board() */
void display_board(int board[] [BOARD_SIZE_VERT])
{
int r = 0;
int c = 0;
printf("\n");
for (r = 0; r < BOARD_SIZE_VERT; ++r)
{
printf ("+");
for (c = 0; c < BOARD_SIZE_HORIZ; ++c) printf("---+");
printf ("\n|");
for (c = 0; c < BOARD_SIZE_HORIZ; ++c)
{
switch (board[c][r])
{
case 0:
printf(" |");
break;
case 1:
printf(" X |");
break;
case 2:
printf(" O |");
break;
default:
printf("Error: board entry %d,%d invaid.\n",r,c);
break;
}
}
printf("\n");
}
printf ("+");
for (c = 0; c < BOARD_SIZE_HORIZ; ++c)
printf("---+");
printf ("\n 1 2 3 4 5 6 7 \n\n");
return;
}
/* Function random_move() */
int random_move(int board[][BOARD_SIZE_VERT], int computer_num)
{
bool valid = false;
int m = 0;
while (!valid)
{
m = (rand() % BOARD_SIZE_HORIZ) + 1;
valid = !is_column_full(board,m);
}
update_board(board, m, computer_num);
return m;
}
/* Function player_move() */
int player_move(int board[][BOARD_SIZE_VERT], int player_num)
{
char c = 'c'; //Initializes c to a non-digit so isdigit(c) returns false
int x = 0;
/* the loop will repeat until the player enters a digit */
while (!isdigit(c))
{
printf("Please enter your move: ");
c = getchar();
while (getchar() != '\n') { } //clears stdin
/* x is the ASCII value of c; if c represents a digit, subtracting '0' *
* will convert the character to the integer it represents. */
x = c - '0';
//Checks whether x is valid, i.e. between 1 and 8
if (x < 1 || x > 8)
{
printf("Not a valid move. Enter a column number!\n");
c = 'c'; //reset c to a non-digit to continue looping
//in case the user entered a digit that was invalid
}
else
{
if (is_column_full(board,x))
{
printf("This column is full. Try again!.\n");
c = 'c';
}
update_board(board, x, player_num);
}
}
return x;
}
/* Function check_win_or_tie() */
bool check_win_or_tie(int board[][BOARD_SIZE_VERT], int last_move)
{
int w;
int c;
int r = 0;
w = check_winner(board, last_move);
if (check_winner(board, last_move))
{
printf("*****************************\n");
printf("* Player %c won!!! Game over *\n", (w == 1 ? 'X' : 'O'));
printf("*****************************\n");
return true;
}
else
{
for (c = 0; c < BOARD_SIZE_VERT; c++)
{
if (board[c][r] == 0) return false;
}
printf("*****************************\n");
printf("* Game is a tie!! No winner *\n");
printf("*****************************\n");
return true;
}
}
/* Function is_column_full() */
bool is_column_full(int board[][BOARD_SIZE_VERT], int m)
{
if (board[m-1][0] == 0)
return false;
else
return true;
}
/* Function update_board() */
void update_board(int board[][BOARD_SIZE_VERT], int m, int player_num)
{
int c, i;
// convert the column number to the array index for that column
c = m - 1;
for (i = BOARD_SIZE_VERT-1; i >= 0; --i)
{
if (board[c][i] != 0) continue;
else
{
board[c][i] = player_num;
return;
}
}
}
/* Function check_winner */
int check_winner(int board[][BOARD_SIZE_VERT], int last_move)
{
int c;
int i;
//again, converts column of last_move to that column's array index
c = last_move - 1;
if (check_diagRU (board, last_move) == true ||
check_diagRD (board, last_move) == true ||
check_horizontal(board, last_move) == true ||
check_vertical (board, last_move) == true )
{
for (i = 0; i < BOARD_SIZE_VERT; i++)
{
switch (board[c][i])
{
case 0:
continue;
case 1:
return 1;
case 2:
return 2;
default:
printf("Error! Value in board[%d][%d] invalid.\n",c,i);
return 0;
}
}
}
return 0;
}
/* Function check_diagRD() */
bool check_diagRD(int board[][BOARD_SIZE_VERT], int last_move)
{
int r; //row index
int c; //column index
for (r = 0; r < 3; r++)
{
for (c = 0; c < 4; c++)
{
if (board[c][r] != 0 &&
board[c][r] == board[c+1][r+1] &&
board[c][r] == board[c+2][r+2] &&
board[c][r] == board[c+3][r+3])
{
return true;
}
else continue;
}
}
return false;
}
/* Function check_diagRU() */
bool check_diagRU(int board[][BOARD_SIZE_VERT], int last_move)
{
int r; //row index
int c; //column index
for (r = 3; r < BOARD_SIZE_VERT; r++)
{
for (c = 0; c < 4; c++)
{
if (board[c][r] != 0 &&
board[c][r] == board[c+1][r-1] &&
board[c][r] == board[c+2][r-2] &&
board[c][r] == board[c+3][r-3])
{
return true;
}
else continue;
}
}
return false;
}
/* Function check_ vertical() */
bool check_vertical(int board[][BOARD_SIZE_VERT], int m)
{
int r; //row index
int c; //column index
for (r = 0; r < 3; r++)
{
for (c = 0; c < BOARD_SIZE_HORIZ; c++)
{
if (board[c][r] != 0 &&
board[c][r] == board[c][r+1] &&
board[c][r] == board[c][r+2] &&
board[c][r] == board[c][r+3])
{
return true;
}
else continue;
}
}
return false;
}
/* Function check_horizontal() checks for 4 in a row */
bool check_horizontal(int board[][BOARD_SIZE_VERT], int m)
{
int r; //row index
int c; //column index
for (r = 0; r < BOARD_SIZE_VERT; r++)
{
for (c = 0; c < 4; c++)
{
if (board[c][r] != 0 &&
board[c][r] == board[c+1][r] &&
board[c][r] == board[c+2][r] &&
board[c][r] == board[c+3][r])
{
return true;
}
else continue;
}
}
return false;
}
/* Function best_move() - To be used in extra credit competition */
int best_move(int board[][BOARD_SIZE_VERT], int computer_num)
{
int playerNum;
int bestMove;
playerNum = (computer_num == 1 ? 2 : 1);
if (check3win(board, computer_num) != 8)
{
bestMove = check3win(board, computer_num);
update_board(board, bestMove, computer_num);
return bestMove;
}
else if (build2(board,computer_num) != 8)
{
bestMove = build2(board, computer_num);
update_board(board, bestMove, computer_num);
return bestMove;
}
/* else if (build1(board,computer_num) != 8)
{
bestMove = build1(board,computer_num);
update_board(board, bestMove, computer_num);
return bestMove;
}*/
else bestMove = random_move(board, computer_num);
return bestMove;
}
/* Function build2() */
int build2(int board[][BOARD_SIZE_VERT], int computer_num)
{
int build2;
if (build2_diagDN(board,computer_num) != 8)
{
build2 = build2_diagDN(board,computer_num);
return build2;
}
if (build2_diagUP(board,computer_num) != 8)
{
build2 = build2_diagUP(board,computer_num);
return build2;
}
if (build2vert(board,computer_num) != 8)
{
build2 = build2vert(board,computer_num);
return build2;
}
if (build2horiz(board,computer_num) != 8)
{
build2 = build2horiz(board,computer_num);
return build2;
}
return 8;
}
/* Function build2_diagDN() */
int build2_diagDN(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c;
int r;
for (r = 0; r < 4; r++)
{
for (c = 0; c < 4; c++)
{
//check X X [] []
if (board[c][r] == computer_num &&
board[c+1][r+1] == computer_num &&
board[c+3][r+3] == 0 &&
board[c+2][r+2] == 0)
{
if (is_column_full(board,c+3)) continue;
if (r == 3 || board[c+2][r+1] != 0) return c+3;
}
//check [] [] X X
if (board[c+2][r+2] == computer_num &&
board[c+3][r+3] == computer_num &&
board[c][r] == 0 &&
board[c+1][r+1] == 0 &&
board[c+1][r+2] != 0)
{
if (is_column_full(board,c+2)) continue;
return c+2;
}
//check [] X X []
if (board[c+2][r+2] == computer_num &&
board[c+1][r+1] == computer_num &&
board[c][r] == 0 &&
board[c+3][r+3] == 0)
{
if (is_column_full(board,c+4)) continue;
if (r == 3 || board[c+3][r+3] != 0) return c+4;
}
//check [] X [] X
if (board[c+1][r+1] == computer_num &&
board[c+3][r+3] == computer_num &&
board[c][r] == 0 &&
board[c+2][r+2] == 0 &&
board[c][r+1] != 0)
{
if (is_column_full(board,c+1)) continue;
return c+1;
}
//check X [] X []
if (board[c+2][r+2] == computer_num &&
board[c][r] == computer_num &&
board[c+1][r+1] == 0 &&
board[c+3][r+3] == 0)
{
if (is_column_full(board,c+4)) continue;
if (r == 3 || board[c+3][r+3] != 0) return c+4;
}
//check X [] [] X
if (board[c][r+1] == computer_num &&
board[c+3][r+3] == computer_num &&
board[c+1][r+1] == 0 &&
board[c+2][r+2] == 0 &&
board[c+2][r+3] != 0)
{
if (is_column_full(board,c+3)) continue;
return c+3;
}
}
}
return 8;
}
/* Function build2_diagUP() */
int build2_diagUP(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c;
int r;
for (r = 3; r < BOARD_SIZE_VERT; r++)
{
for (c = 0; c < 4; c++)
{
//check X X [] []
if (board[c][r] == computer_num &&
board[c+1][r-1] == computer_num &&
board[c+3][r-3] == 0 &&
board[c+2][r-2] == 0 &&
board[c+3][r-2] != 0)
return c+4;
//check [] [] X X
if (board[c+2][r-2] == computer_num &&
board[c+3][r-3] == computer_num &&
board[c][r] == 0 &&
board[c+1][r-1] == 0)
{
if (r == BOARD_SIZE_VERT || board[c][r+1] != 0) return c+1;
}
//check [] X X []
if (board[c+2][r-2] == computer_num &&
board[c+1][r-1] == computer_num &&
board[c][r] == 0 &&
board[c+3][r-3] == 0 &&
board[c+3][r-2] != 0)
return c+4;
//check [] X [] X
if (board[c+1][r-1] == computer_num &&
board[c+3][r-3] == computer_num &&
board[c][r] == 0 &&
board[c+2][r-2] == 0)
{
if (r == BOARD_SIZE_VERT || board[c][r+1] != 0) return c+1;
}
//check X [] X []
if (board[c+2][r-2] == computer_num &&
board[c][r] == computer_num &&
board[c+1][r-1] == 0 &&
board[c+3][r-3] == 0 &&
board[c+3][r-2] != 0)
return c+4;
//check X [] [] X
if (board[c][r+1] == computer_num &&
board[c+3][r-3] == computer_num &&
board[c+1][r-1] == 0 &&
board[c+2][r-2] == 0 &&
board[c+1][r-2] != 0)
return c+2;
}
}
return 8;
}
/* Function build2vert() */
int build2vert(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c;
int r;
for (r = 2; r < BOARD_SIZE_VERT; r++)
{
for (c = 0; c < BOARD_SIZE_HORIZ; c++)
{
if (is_column_full(board,c))
{
printf("column %d full. Skipping\n", c);
continue;
}
if (board[c][r-1] == 0 &&
board[c][r] == computer_num &&
board[c][r+1] == computer_num)
{
return c+1;
}
}
}
return 8;
}
/* Function build2horiz() */
int build2horiz(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c;
int r;
for (r = 5; r >= 0; r--)
{
for (c = 0; c < 4; c++)
{
//check X X [] []
if (board[c+3][r] == 0 &&
board[c+2][r] == 0 &&
board[c+1][r] == computer_num &&
board[c][r] == computer_num)
{
if (r == 5 || board[c+3][r+1] != 0) return c+4;
}
//check [] [] X X
if (board[c][r] == 0 &&
board[c+1][r] == 0 &&
board[c+2][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c][r+1] != 0) return c+1;
}
//check [] X X []
if (board[c][r] == 0 &&
board[c+3][r] == 0 &&
board[c+2][r] == computer_num &&
board[c+1][r] == computer_num)
{
if (r == 5 || board[c+1][r+1] != 0) return c+1;
}
//check X [] X []
if (board[c+3][r] == 0 &&
board[c+1][r] == 0 &&
board[c+2][r] == computer_num &&
board[c][r] == computer_num)
{
if (r == 5 || board[c+3][r+1] != 0) return c+4;
}
//check [] X [] X
if (board[c][r] == 0 &&
board[c+2][r] == 0 &&
board[c+1][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c][r+1] != 0) return c+1;
}
//check X [] [] X
if (board[c+1][r] == 0 &&
board[c+2][r] == 0 &&
board[c][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c+1][r+1] != 0) return c+2;
}
}
}
return 8;
}
/* Function check3win() */
int check3win(int board[][BOARD_SIZE_VERT], int computer_num)
{
int winMove;
int blockMove;
int playerNum;
playerNum = (computer_num == 1 ? 2 : 1);
if (check3_vertwin(board, computer_num) != 8) {
winMove = check3_vertwin(board, computer_num);
return winMove;
}
if (check3_horizwin(board, computer_num) != 8) {
winMove = check3_horizwin(board, computer_num);
return winMove;
}
if (check3_diagRUwin(board, computer_num) != 8) {
winMove = check3_diagRUwin(board, computer_num);
return winMove;
}
if (check3_diagRDwin(board, computer_num) != 8) {
winMove = check3_diagRDwin(board, computer_num);
return winMove;
}
if (check3_vertwin(board, playerNum) != 8) {
blockMove = check3_vertwin(board, playerNum);
return blockMove;
}
if (check3_horizwin(board, playerNum) != 8) {
blockMove = check3_horizwin(board, playerNum);
return blockMove;
}
if (check3_diagRUwin(board, playerNum) != 8) {
blockMove = check3_diagRUwin(board, playerNum);
return blockMove;
}
if (check3_diagRDwin(board, playerNum) != 8) {
blockMove = check3_diagRDwin(board, playerNum);
return blockMove;
}
return 8;
}
/* Function check3_vertwin() */
int check3_vertwin(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c; //column index
int r; //row index
//start checking from 5th row; can't make a move if top row full
for (r = 1; r < 4; r++)
{
for (c = 0; c < BOARD_SIZE_HORIZ; c++)
{
if (is_column_full(board,c))
{
printf("Column %d full. Skipping\n", c);
continue;
}
if (board[c][r-1] == 0 &&
board[c][r] == computer_num &&
board[c][r+1] == computer_num &&
board[c][r+2] == computer_num)
{
return c+1; //changed from c
}
}
}
return 8;
}
/* Function check3_horizwin() */
int check3_horizwin(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c; //column index
int r; //row index
for (r = 5; r >= 0; r--)
{
for (c = 0; c < 5; c++)
{
//check X X X []
if (board[c+3][r] == 0 &&
board[c][r] == computer_num &&
board[c+1][r] == computer_num &&
board[c+2][r] == computer_num)
{
if (r == 5 || board[c+3][r+1] != 0) return c+4; //changed from 3
}
//check X [] X X
if (board[c+1][r] == 0 &&
board[c][r] == computer_num &&
board[c+2][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c+1][r+1] != 0) return c+2; //changed from 1
}
//check X X [] X
if (board[c+2][r] == 0 &&
board[c][r] == computer_num &&
board[c+1][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c+2][r+1] != 0) return c+3; //changed from 2
}
//check [] X X X
if (board[c][r] == 0 &&
board[c+1][r] == computer_num &&
board[c+2][r] == computer_num &&
board[c+3][r] == computer_num)
{
if (r == 5 || board[c][r+1] != 0) return c+1; //changed from 3
}
}
}
return 8;
}
/* Function check3_diagRDwin() */
int check3_diagRDwin(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c; //column index
int r; //row index
for (r = 0; r < 4; r++)
{
for (c = 0; c < 4; c++)
{
//check X X X []
if (board[c+3][r+3] == 0 &&
board[c][r] == computer_num &&
board[c+1][r+1] == computer_num &&
board[c+2][r+2] == computer_num)
{
if (r == 3 || board[c+3][r+4] == 1 || board[c+3][r+4] == 2) return c+4;
}
//check X X [] X
if (board[c+2][r+2] == 0 &&
board[c][r] == computer_num &&
board[c+1][r+1] == computer_num &&
board[c+3][r+3] == computer_num &&
board[c+2][r+3] != 0 )
{
return c+3;
}
//check X [] X X
if (board[c+1][r+1] == 0 &&
board[c][r] == computer_num &&
board[c+2][r+2] == computer_num &&
board[c+3][r+3] == computer_num &&
board[c+1][r+2] != 0)
{
return c+2;
}
//check [] X X X
if (board[c][r] == 0 &&
board[c+1][r+1] == computer_num &&
board[c+2][r+2] == computer_num &&
board[c+3][r+3] == computer_num)
{
if (r == 5 || board[c][r+1] != 0) return c+1; //changed from 3
}
}
}
return 8;
}
/* Function check3_diagRUwin() */
int check3_diagRUwin(int board[][BOARD_SIZE_VERT], int computer_num)
{
int c; //column index
int r; //row index
for (r = 3; r < BOARD_SIZE_VERT; r++)
{
for (c = 0; c < 4; c++)
{
//check X X X []
if (board[c+3][r-3] == 0 &&
board[c][r] == computer_num &&
board[c+1][r-1] == computer_num &&
board[c+2][r-2] == computer_num &&
board[c+3][r-2] != 0)
{
return c+4; //changed from 3?
}
//check X X [] X
if (board[c+2][r-2] == 0 &&
board[c][r] == computer_num &&
board[c+1][r-1] == computer_num &&
board[c+3][r-3] == computer_num &&
board[c+2][r-1] != 0)
{
return c+3; //changed from 2?
}
//check X [] X X
if (board[c+1][r-1] == 0 &&
board[c][r] == computer_num &&
board[c+2][r-2] == computer_num &&
board[c+3][r-3] == computer_num &&
board[c+1][r] != 0)
{
return c+2; //changed from 1?
}
//check [] X X X
if (board[c][r] == 0 &&
board[c+1][r+1] == computer_num &&
board[c+2][r+2] == computer_num &&
board[c+3][r+3] == computer_num)
{
if (r == 5 || board[c][r+1] != 0) return c+1; //changed from 3
}
}
}
return 8;
}
/* End of file */
Header file - connect4_functions.h (provided by the assignment - just some #includes, #defines, and forward declarations of the functions we were required to write [the other declarations are in c4_functions.c)
#ifndef CONNECT4_FUNCTIONS
#define CONNECT4_FUNCTIONS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#define BOARD_SIZE_HORIZ 7
#define BOARD_SIZE_VERT 6
/******************************************************************************
* Function print_welcome() *
*******************************************************************************
* *
* This function does not take any input. It prints a welcome message for *
* Alice, asks her if she would like to make the first move, reads the input *
* from stdin, and clears the input buffer (in case Alice enters more than one *
* character). The function returns 2 if the (first) character typed by Alice *
* is either n or N. In all other cases, the function returns 1. *
* *
******************************************************************************/
int print_welcome(void);
/******************************************************************************
* Function display_board() *
*******************************************************************************
* *
* This function receives the board array as input, and prints the current *
* state of the board to stdout. The function expects the value of every cell *
* in the board array to be either 0, 1, or 2, where 1 denotes stones of the *
* first player (printed as X) while 2 denotes stones of the second player *
* (printed as O). A cell whose value is 0 denotes a place on the board that *
* is not occupied by a stone of either player. *
* *
* The format of the print-out follows the example below. The width of every *
* cell is three characters, and the stone occupying this cell (if any) is the *
* middle character. Vertical lines separate between cells in the same row, *
* while the rows themselves are separated by a line of hyphens along with '+' *
* characters. Right under the board, the function prints the indices of the *
* columns, with each such index centered in its column. *
* *
* +---+---+---+---+---+---+---+ *
* | | | | | | | | *
* +---+---+---+---+---+---+---+ *
* | O | | | | | | | *
* +---+---+---+---+---+---+---+ *
* | X | | | | | | | *
* +---+---+---+---+---+---+---+ *
* | O | | | | | | | *
* +---+---+---+---+---+---+---+ *
* | O | | | | | | | *
* +---+---+---+---+---+---+---+ *
* | O | X | | X | | | X | *
* +---+---+---+---+---+---+---+ *
* 1 2 3 4 5 6 7 *
* *
******************************************************************************/
void display_board(int board[][BOARD_SIZE_VERT]);
/******************************************************************************
* Function random_move() *
*******************************************************************************
* *
* This function receives the board array and player number as input. It then *
* makes a valid random move. To this end, the function generates uniformly at *
* random an integer m in the range 1,2, ..., BOARD_SIZE_HORIZ using a call to *
* the rand() standard library function. It then verifies that this integer m *
* constitutes a valid move, by calling the function is_column_full(). If m is *
* a valid move, the function returns m. If not (that is, if the m-th column *
* is full, the function repeat the process until a valid move is generated by *
* rand(). Note that the function assumes that at least one cell in the array *
* board is 0; otherwise it enters into an infinite~loop! *
* *
* Prior to returning m, the function also updates the state of the board by *
* making the function call update_board(board,m,computer_num). *
* *
******************************************************************************/
int random_move(int board[][BOARD_SIZE_VERT], int computer_num);
/******************************************************************************
* Function player_move() *
*******************************************************************************
* *
* This function receives the board array and player number as input. It then *
* prompts Alice to enter her move, reads her input from stdin, and clears the *
* input buffer. If Alice enters anything other than an integer in the range *
* 1,2, ..., BOARD_SIZE_HORIZ, the function prints "Not a valid move. Enter a *
* column number!" to stdout and prompts Alice again to enter a move. If Alice *
* enters an integer m in the appropriate range, the function verifies that *
* the corresponding column is not full by calling is_column_full(). If the *
* column is full, the function prints "This column is full. Try again!" and *
* again prompt Alice to enter a move. When Alice does (eventually) enter *
* a valid move m, the function updates the state of the board using the call *
* update_board(board,m,player_num) and then returns m. *
* *
******************************************************************************/
int player_move(int board[][BOARD_SIZE_VERT], int player_num);
/******************************************************************************
* Function check_win_or_tie() *
*******************************************************************************
* *
* This function receives as input the board array and an integer last_move, *
* which is interpreted as the index of the column where the most recent stone *
* was played. The function calls check_winner() to determine whether the game *
* has been won by either player. If so, the function prints either "Player X *
* won!" or "Player O won!" and returns true. If there is no winner, the func- *
* tion checks whether the game is drawn: no spaces left on the board. If so, *
* the function prints "Tie game!" and returns true. Otherwise, the function *
* returns false, indicating that the game is not yet over. *
* *
******************************************************************************/
bool check_win_or_tie(int board[][BOARD_SIZE_VERT], int last_move);
/******************************************************************************
* Function is_column_full() *
*******************************************************************************
* *
* This function receives as input the board array and an integer m, which is *
* is expected to be in the range 1,2, ..., BOARD_SIZE_HORIZ. The function *
* returns true if the m-th column of the board is full, and false otherwise. *
* *
******************************************************************************/
bool is_column_full(int board[][BOARD_SIZE_VERT], int m);
/******************************************************************************
* Function update_board() *
*******************************************************************************
* *
* This function receives as input the board array, an integer m which is *
* expected to be in the range 1,2, ..., BOARD_SIZE_HORIZ, and an integer *
* player_num which should be either 1 or 2. It then updates the board by *
* changing the appropriate entry in the m-th column from 0 to player_num. *
* Note that "the m-th column of the board" actually refers to board[m-1][]. *
* *
* The function determine which row in the m-th column to update using the *
* rule that a stone dropped into a given column always slides down to the *
* lowest unoccupied row in that column. *
* *
******************************************************************************/
void update_board(int board[][BOARD_SIZE_VERT], int m, int player_num);
/******************************************************************************
* Function check_winner() *
*******************************************************************************
* *
* This function receives as input the board array and an integer last_move, *
* which is interpreted as the index of the column where the most recent stone *
* was played. The function checks whether the placement of this most recent *
* stone results in a win (4 stones on the board forming a consecutive horiz- *
* ontal, vertical, or diagonal sequence). If so, the function returns the *
* player number (either 1 or 2) of the winning player. If there is no winner, *
* the function returns 0. *
* *
******************************************************************************/
int check_winner(int board[][BOARD_SIZE_VERT], int last_move);
/******************************************************************************
* Function best_move() *
*******************************************************************************
* *
* This EXTRA-CREDIT function receives the board array and a player number as *
* input. It returns the best (valid) move m for the given board position. *
* *
* Prior to returning m, the function also updates the state of the board by *
* making the function call update_board(board,m,computer_num). *
* *
******************************************************************************/
int best_move(int board[][BOARD_SIZE_VERT], int computer_num);
#endif
Program connect4best.c (this was provided, and we weren't allowed to touch it. This version is actually slightly edited, because the original called random_move() instead of best_move() for the computer's turn, and best_move() is what I'm working on here)
#include "connect4_functions.h"
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
This program plays a game of Connect-4 between the user (herein called Alice)
and the computer. The declarations of all the functions used in this program
are in connect4_functions.h. The definitions of these functions are expected
in c4_functions.c.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
int main()
{
int board[BOARD_SIZE_HORIZ][BOARD_SIZE_VERT] = { {0} };
int player_num, computer_num;
int last_move;
/* Ask Alice if she wants to go first */
player_num = print_welcome();
if (player_num == 1) computer_num = 2;
else computer_num = 1;
/* If Alice wants to go first, let her make a move */
if (player_num == 1)
{
display_board(board);
last_move = player_move(board,player_num);
display_board(board);
}
/* The main loop */
while (1)
{
/* Make a computer move, then display the board */
last_move = best_move(board,computer_num);
printf("Computer moved in column: %d\n", last_move);
display_board(board);
/* Check whether the computer has won */
if (check_win_or_tie(board,last_move)) return 0;
/* Let Alice make a move, then display the board */
last_move = player_move(board,player_num);
display_board(board);
/* Check whether Alice has won */
if (check_win_or_tie(board,last_move)) return 0;
} /* end of while (1) */
} /* end of main() */
/******************************************************************************
End of file
******************************************************************************/