Now that we know how to start and continue the game, let’s implement the second stage, where we decide how and when to stop the game.

When does the game stop?

  1. When either player wins the game.
  2. When the game is drawn.

So let’s incorporate the stopping conditions in our code.

Stage 2 implementation

Extending the memory

To declare a game to be over, we need to check whether someone has won the game after each turn.

We would need a bool variable called gameOver, which can initially be set to false. We can have a variable called winner set to -1 initially.

Checking for the winner

After displaying the updated board (revised), we can create a function that checks whether we have a winner after each turn. Let’s call it the isWin() function.

We would need a winCount (to see if the win count of a certain symbol has been reached), pSym symbol (to check that particular symbol for win count), the array for the board Board[][CAPACITY] (on which we are playing) of the dimension dim. Finally, we would also need the row and column indices of the position (ri and ci).

Our function prototype would then be as follows:

bool isWin(char Board[][CAPACITY], int  dim, int winCount, char pSym, int ri, int ci)

As for its implementation, we might need to check for the winCount in all directions.

Here comes the interesting part. You’d would naturally think that to see if a player has won or not after each turn, we would need to check for the winCount number of consecutive symbols vertically up and down, horizontally left and right, as well as left and right diagonally (in all directions).

However, that is not the case. After each turn, if we were to run a loop that checks for winCount symbols starting from the first cell to the last cell of the grid, then we would not need to check for the symbols vertically upwards and horizontally towards the left. This is because each cell’s vertically upward and horizontally left cells would have already been checked.

Let’s see the animation below for better understanding.

So, our isWin() function iterates over all cells and invokes the win-checking method, isWinHere(), on each cell. The following is the implementation of the isWin() function:

Press + to interact
bool isWin(char Board[][CAPACITY], int dim, int winCount, char pSym)
{
for (int ri = 0; ri < dim; ri++)
{
for (int ci = 0; ci < dim; ci++)
{
if (isWinHere(Board, dim, winCount, pSym, ri, ci))
return true;
}
}
return false;
}

Consequently, our implementation of the isWinHere() function is as follows:

Press + to interact
bool isWinHere(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
bool horizontal = horizontalCheck(Board, dim, winCount, pSym, ri, ci);
bool vertical = vericalCheck(Board, dim, winCount, pSym, ri, ci);
bool leftDiagonal = leftDiagonalCheck(Board, dim, winCount, pSym, ri, ci);
bool rightDiagonal = rightDiagonalCheck(Board, dim, winCount, pSym, ri, ci);
// return true if either direction contains winCount number of the same symbol i.e. we have a winner
return (horizontal || vertical || leftDiagonal || rightDiagonal);
}

Now, let’s implement the direction functions.

a. Checking win horizontally

Starting with the bool function, horizontalCheck(), we want to check if there are winCount number of symbols from left to right. So, let’s write a for loop that runs from 0 times to winCount.

Now, when checking horizontally, what has changed in terms of rows and columns?

The row remains the same but the number of columns increases, right? So, inside this for loop, we’ll check for the same symbol in the row ri and increasing columns ci + i where i = 0 and less than winCount of array Board[][]. If within this loop, the horizontal cells do not contain the symbol, we return false, else, we return true. Look at the code below:

Press + to interact
bool horizontalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
for (int i = 0; i < winCount; i++)
{
if (Board[ri][ci + i] != pSym)
return false;
}
return true;
}

There’s still a little concern we need to address. What if the horizontal cell being checked is in the last column? Think of any right-most cell of our grid. If we were to check horizontally from that position, it would check for the winCount number of cells outside our grid. We don’t want that. So, let’s add a condition before the for loop that checks if the ci + winCount - 1 is greater than or equal to the dimension dim. If it is, we return false.

So the function becomes:

Press + to interact
bool horizontalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
if (ci + winCount - 1 >= dim)
return false;
for (int i = 0; i < winCount; i++)
{
if (Board[ri][ci + i] != pSym)
return false;
}
return true;
}

b. Checking win vertically

Our function, verticalCheck(), is very similar to the function above, except it checks for the winCount number of symbols vertically. Try and write its implementation in the editor below.

Your function should return true if there are vertical winCount number of symbols.

Press + to interact
#include <iostream>
using namespace std;
bool verticalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
// Write code here.
return false;
}

Click the “Show Solution” button below to see its solution:

c. Checking win diagonally 1 (from top-left to down-right)

Next, we have the rightDiagonalCheck() function that checks for the winCount number of symbols from left to right diagonally.

Again, this function is also very similar to the previous one. Except here, both the number of rows and columns increase as we go down diagonally.

Try and write its implementation in the editor below:

Press + to interact
bool rightDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
// Write code here.
}

Click the “Show Solution” button below to see its solution:

d. Checking win diagonally 2 (from top-right to down-left)

Similarly, the leftDiagonalCheck() function checks for the winCount number of symbols from right to left diagonally.

In this direction, the number of rows increases, and the number of columns decreases as we go down diagonally from right to left.

Press + to interact
bool leftDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
if (ci - (winCount - 1) < 0)
return false;
for (int i = 0; i < winCount; i++)
{
if (Board[ri + i][ci - i] != pSym)
return false;
}
return true;
}

Checking for the game drawn

Now, let’s check for the other game-stopping conditions to see if the game is drawn or not.

For this, we can simply return false if any cell is equal to the dash -. If none equals the dash, then we return true.

So the function would be:

Press + to interact
bool isDraw(char Board[][CAPACITY], int dim)
{
for (int ri = 0; ri < dim; ri++)
{
for (int ci = 0; ci < dim; ci++)
{
if (Board[ri][ci] == '-')
return false;
}
}
return true;
}

Changes in the main-flow

Let us incorporate main() function to handle these three things one by one.

  • Winning conditions

  • Match-drawn condition

  • Output appropriate message when the game ends

  • Converting the game into a tournament format will allow players to play repeatedly after each game, with the option to continue or end the tournament being prompted after each game

Winning conditions

After the updateBoard() function call in main(), we’ll invoke the isWin() function and store the result in the gameOver variable. Secondly, we’ll also add a condition that if gameOver is true, we set the winner variable to the current turn.

The changes have been highlighted below:

Press + to interact
int main() {
// Initializing memory
.
.
.
do {
.
.
.
updateBoard(Board, dim, ri, ci, pSym[turn]);
gameOver = isWin(Board, dim, winCount, pSym[turn]);
if (gameOver)
winner = turn;
} while(true);
}

Match-drawn conditions

Next, we’ll invoke the isDraw() function and if it returns true, we’ll set the gameOver variable to true.

We add the following code after the winning condition:

Press + to interact
int main() {
// Initializing memory
.
.
.
do {
.
.
.
updateBoard(Board, dim, ri, ci, pSym[turn]);
gameOver = isWin(Board, dim, winCount, pSym[turn]);
if (gameOver)
winner = turn;
else if (isDraw(Board, dim))
gameOver = true;
} while(true);
}

Finally, we also need to make sure that the game progresses (meaning the turn changes while gameOver is false ) and terminates when gameOver is true. Therefore, we do it in the following way:

Press + to interact
int main() {
// Initializing memory
.
.
.
do {
.
.
.
updateBoard(Board, dim, ri, ci, pSym[turn]);
gameOver = isWin(Board, dim, winCount, pSym[turn]);
if (gameOver)
winner = turn;
else if (isDraw(Board, dim))
gameOver = true;
if (!gameOver)
turnChange(turn, NOP);
} while(!gameOver);
}

Output appropriate message when game ends

After the while loop ends, we utilize the winner variable to determine whether the game was drawn (if the value is -1) or game was won (if the value is not -1), and print an appropriate message as follows:

Press + to interact
int main() {
// Initializing memory
.
.
.
do {
.
.
.
} while(!gameOver);
cout << endl;
if (winner == -1)
cout << "Game Draw!" << endl;
else
{
cout << pName[turn] << " has won!" << endl;
winnerCount[winner]++;
}
}

Convert game into a tournament

To convert the game into a tournament, we simply enclose the existing do-while loop inside another one, that based on the user’s choice—either re-runs it or terminates it. Lastly, we show player stats at the end.

Press + to interact
int main() {
// Initializing memory
.
.
.
do {
do {
.
.
.
} while(!gameOver);
.
.
. // input choice (which should be declared in the memory part)
}
while (choice == 'y'|| choice == 'Y');
for (int i = 0; i < 2; i++)
{
cout << "Player" << pName[i] << " has won " << winnerCount[i] << " times";
}
}

Placing it together

The following is the complete code:

#include <iostream>
#include <iomanip>
#include <time.h>
using namespace std;

#define CAPACITY 100

void init(char Board[][CAPACITY], int& dim, char pName[2][30], char pSym[], int&NOP, int& turn, int& winCount);
void printBoard(char Board[][CAPACITY], int  dim);
void userInput(int& ri, int& ci, char pName[], char pSym);
bool validInput(int ri, int ci, char Board[][CAPACITY], int dim);
void updateBoard(char Board[][CAPACITY], int  dim, int ri, int ci, char pSym);
bool horizontalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci);
bool vericalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci);
bool rightDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci);
bool leftDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci);
bool isWinHere(char Board[][CAPACITY], int  dim, int winCount, char pSym, int ri, int ci);
bool isWin(char Board[][CAPACITY], int  dim, int winCount, char pSym);
bool isDraw(char Board[][CAPACITY], int dim);
void turnChange(int& turn, int NOP);

int main()
{
    cout <<"\n\n\t\t\tThe Game of Gomoku...!!!\n\n";
    cout <<"\n\n\t\t\t    Let us play...!\n\n"<<endl;
	srand(time(0));
	char choice;
	char Board[CAPACITY][CAPACITY];
	char pName[2][30];
	char pSym[2];
	int turn, ri, ci, dim, NOP, winCount;
	bool gameOver = false;
	int winnerCount[2] = { 0, 0 };
	int winner = -1;
	do
	{
	    cout <<"Initializing...!!!"<<endl;
		init(Board, dim, pName, pSym, NOP, turn, winCount);
		do
		{
			printBoard(Board, dim);

			do
			{
				userInput(ri, ci, pName[turn], pSym[turn]);
				if (!validInput(ri, ci, Board, dim))
				{
					cout << "Invalid Input" << endl;

				}
			} while (!validInput(ri, ci, Board, dim));
			updateBoard(Board, dim, ri, ci, pSym[turn]);
			gameOver = isWin(Board, dim, winCount, pSym[turn]);
			if (gameOver)
				winner = turn;
			else if (isDraw(Board, dim))
				gameOver = true;
			if (!gameOver)
				turnChange(turn, NOP);
		} 
		while (!gameOver);
		cout << endl;
		if (winner == -1)
			cout << "Game Draw!" << endl;
		else
		{
			cout << pName[turn] << " has won!" << endl;
			winnerCount[winner]++;
		}
		cout << "Do you want to play again? y/n ";
		cin >> choice;
	} 
	while (choice == 'y'|| choice == 'Y');
	for (int i = 0; i < 2; i++)
	{
		cout << "Player" << pName[i] << " has won " << winnerCount[i] << " times";
	}
}


void init(char Board[][CAPACITY], int& dim, char pName[2][30], char pSym[], int&NOP, int& turn, int& winCount)
{
	cout << "Dimension: ";
	cin >> dim;
	cout << "Win Count: ";
	cin >> winCount;
	cout << "# of Players: ";
	cin >> NOP;
	for (int i = 0; i < NOP; i++)
	{
		cout << "Enter player " << i + 1 << "'s name: ";
		cin >> pName[i]; // or cin.getline(Pname[i])
	}
	for (int i = 0; i < NOP; i++)
	{
		cout << "Enter player " << i + 1 << "'s symbol: ";
		cin >> pSym[i];  
	}
	for (int ri = 0; ri < dim; ri++)
	{
		for (int ci = 0; ci < dim; ci++)
		{
			Board[ri][ci] = '-';
		}
	}
	turn = rand() % 2;
}
void printBoard(char Board[][CAPACITY], int  dim)
{
	system("clear");   // system("clear");
	for (int ri = 0; ri < dim; ri++)
	{
		for (int ci = 0; ci < dim; ci++)
		{
			cout << Board[ri][ci] << "   ";
		}
		cout << endl;
	}
}
void userInput(int& ri, int& ci, char pName[], char pSym)
{
	cout << pName << "'s Turn to place '" << pSym << " ' (ri, ci): " ;
	cin >> ri;
	cin >> ci;
	ri--;
	ci--;
}
bool validInput(int ri, int ci, char Board[][CAPACITY], int dim)
{
	return ((ri >= 0 && ci >= 0) && (ri <= dim && ci <= dim) && Board[ri][ci] == '-');
}
void updateBoard(char Board[][CAPACITY], int  dim, int ri, int ci, char pSym)
{
	Board[ri][ci] = pSym;
}
bool horizontalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
	if (ci + winCount - 1 >= dim)
		return false;
	for (int i = 0; i < winCount; i++)
	{
		if (Board[ri][ci + i] != pSym)
			return false;
	}
	return true;
}
bool vericalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
	if (ri + winCount - 1 >= dim)
		return false;
	for (int i = 0; i < winCount; i++)
	{
		if (Board[ri + i][ci] != pSym)
			return false;
	}
	return true;
}
bool rightDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
	if (ri + winCount - 1 >= dim)
		return false;
	for (int i = 0; i < winCount; i++)
	{
		if (Board[ri + i][ci + i] != pSym)
			return false;
	}
	return true;
}
bool leftDiagonalCheck(char Board[][CAPACITY], int dim, int winCount, char pSym, int ri, int ci)
{
	if (ci - (winCount - 1) < 0)
		return false;
	for (int i = 0; i < winCount; i++)
	{
		if (Board[ri + i][ci - i] != pSym)
			return false;
	}
	return true;

}
bool isWinHere(char Board[][CAPACITY], int  dim, int winCount, char pSym, int ri, int ci)
{
	bool horizontal = horizontalCheck(Board, dim, winCount, pSym, ri, ci);
	bool vertical = vericalCheck(Board, dim, winCount, pSym, ri, ci);
	bool leftDiagonal = leftDiagonalCheck(Board, dim, winCount, pSym, ri, ci);
	bool rightDiagnal = rightDiagonalCheck(Board, dim, winCount, pSym, ri, ci);
	return (horizontal || vertical || leftDiagonal || rightDiagnal);
}
bool isWin(char Board[][CAPACITY], int  dim, int winCount, char pSym)
{
	for (int ri = 0; ri < dim; ri++)
    {
        for (int ci = 0; ci < dim; ci++)
        {
            if (isWinHere(Board, dim, winCount, pSym, ri, ci))
                return true;
        }
    }

    return false;
}
bool isDraw(char Board[][CAPACITY], int dim)
{
	for (int ri = 0; ri < dim; ri++)
	{
		for (int ci = 0; ci < dim; ci++)
		{
			if (Board[ri][ci] == '-')
				return false;
		}
	}
	return true;
}
void turnChange(int& turn, int NOP)
{
	turn = (turn + 1) % NOP;
}

Complete code of Gomoku