Creating a Gomoku Game in C

CCBeginner
Practice Now

Introduction

In this project, we will create a simple text-based Gomoku game using the C programming language. Gomoku is a two-player strategy board game where the objective is to be the first to get five consecutive pieces in a row, either horizontally, vertically, or diagonally. We will develop this game using a 15x15 game board.

👀 Preview

Gomoku Game

🎯 Tasks

In this project, you will learn:

  • How to design and implement a chessboard using a two-dimensional array in C.
  • How to create a main function to control the flow of the game.
  • How to implement functions to initialize the game, print the chessboard, and allow players to play their turns.
  • How to develop a function to check for a winning condition in the game.
  • How to compile and run the program.

🏆 Achievements

After completing this project, you will be able to:

  • Work with two-dimensional arrays in C.
  • Design and implement a game flow using functions.
  • Check for winning conditions in a game.
  • Compile and run a C program.

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL c(("`C`")) -.-> c/UserInteractionGroup(["`User Interaction`"]) c(("`C`")) -.-> c/BasicsGroup(["`Basics`"]) c(("`C`")) -.-> c/ControlFlowGroup(["`Control Flow`"]) c(("`C`")) -.-> c/PointersandMemoryGroup(["`Pointers and Memory`"]) c(("`C`")) -.-> c/FunctionsGroup(["`Functions`"]) c/UserInteractionGroup -.-> c/output("`Output`") c/BasicsGroup -.-> c/comments("`Comments`") c/BasicsGroup -.-> c/variables("`Variables`") c/BasicsGroup -.-> c/data_types("`Data Types`") c/BasicsGroup -.-> c/constants("`Constants`") c/BasicsGroup -.-> c/operators("`Operators`") c/ControlFlowGroup -.-> c/if_else("`If...Else`") c/ControlFlowGroup -.-> c/while_loop("`While Loop`") c/ControlFlowGroup -.-> c/for_loop("`For Loop`") c/ControlFlowGroup -.-> c/break_continue("`Break/Continue`") c/UserInteractionGroup -.-> c/user_input("`User Input`") c/PointersandMemoryGroup -.-> c/memory_address("`Memory Address`") c/PointersandMemoryGroup -.-> c/pointers("`Pointers`") c/FunctionsGroup -.-> c/function_parameters("`Function Parameters`") c/FunctionsGroup -.-> c/function_declaration("`Function Declaration`") c/FunctionsGroup -.-> c/recursion("`Recursion`") subgraph Lab Skills c/output -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/comments -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/variables -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/data_types -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/constants -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/operators -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/if_else -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/while_loop -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/for_loop -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/break_continue -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/user_input -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/memory_address -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/pointers -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/function_parameters -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/function_declaration -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} c/recursion -.-> lab-298828{{"`Creating a Gomoku Game in C`"}} end

Create Project Files

First, create a new file named gomoku.c and open it in your preferred code editor.

cd ~/project
touch gomoku.c

Designing a Chessboard

Using 'O' and 'X' to represent chess pieces

Firstly, we need a chessboard (15 * 15) to record the "situation" of each position on the board. So we can define an array chessboard[16][16]. Why not [15][15]? It is because this way, the coordinates of the array can correspond exactly to the rows and columns of the chessboard, making it easier to write the subsequent code.

#include <stdio.h>

#define N 15

// Define an array and assign 0 as the initial value to each element.
int chessboard[N + 1][N + 1] = { 0 };

main Function

Before starting to write the main function, let's briefly consider the typical flow of a game. First, we enter the main interface of the game, then click the start button to enter the game, then display the game screen, determine the win or loss, and end the game. So, what is the flow of a game like Go?

First of all, we enter the welcome screen of the game, then input Y to start the game (not Y to quit the game), display the game board, and have two players take turns to place their pieces, then determine the win or loss (whether there are 5 pieces in a row).

// It's used to keep track of whether it's player 1's turn or player 2's turn, with an odd number representing player 1's turn and an even number representing player 2's turn.
int whoseTurn = 0;

int main(void)
{
	// A custom function that initializes the game, i.e. displays the welcome screen and enters the game display board.
	initGame();

	// This loop is for two players to take turns.
	while (1)
	{
		// Each cycle increments by 1, so that two people can take turns.
		whoseTurn++;

		// A custom function that performs a drop operation.
		playChess();
	}

	return 0;
}

initGame Function

In this function, the features we want to implement are:

  • Display a simple welcome screen.
  • Request input 'Y' and display the chessboard after input.
void initGame(void)
{
	char c;

	printf("Please input 'y' to enter the game:");
	c = getchar();
	if ('y' != c && 'Y' != c)
		exit(0);

	//Clear
	system("clear");

	//Here again we call a custom function whose function is to print out the chessboard.
	printChessboard();
}

In the initGame function, we use the functions exit and system, so we need to include the header file stdlib.h at the top of the program.

#include <stdlib.h>

Function printChessboard

In this function, our objective is to:

  • Print the row and column numbers, and print the chessboard.
  • If the value of the array element is 0, print an asterisk (*) to indicate that the position is empty.
  • If the value of the array element is 1, print a solid circle (X), representing player 1's piece.
  • If the value of the array element is 2, print an empty circle (O), representing player 2's piece.
void printChessboard(void)
{
	int i, j;

	for (i = 0; i <= N; i++)
	{
		for (j = 0; j <= N; j++)
		{
			if (0 == i)		//This prints out the column number.
				printf("%3d", j);
			else if (j == 0)	//Print row number.
				printf("%3d", i);
			else if (1 == chessboard[i][j])
				printf("  X");
			else if (2 == chessboard[i][j])
				printf("  O");
			else
				printf("  *");
		}
		printf("\n");
	}
}

playChess Function

In this function, we want to achieve the following:

  • Prompt the player to input the position for placing the chess piece.
  • If it is currently player 1's turn, assign the value of 1 to the corresponding element in the array.
  • If it is currently player 2's turn, assign the value of 2 to the corresponding element in the array.
  • After each move, determine if the current player has won.
void playChess(void)
{
	int i, j, winner;

	//Determine if it is player 1's turn or player 2's turn, and then assign the value to the corresponding element in the array.
	if (1 == whoseTurn % 2)
	{
		printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
		scanf("%d %d", &i, &j);
		chessboard[i][j] = 1;
	}
	else
	{
		printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
		scanf("%d %d", &i, &j);
		chessboard[i][j] = 2;
	}

	//Print the board again.
	system("clear");
	printChessboard();	//This function is called again.

	/*
	*The following section calls the custom function (the judge function).
	*It is used to determine whether the current player has won the move or not.
	*/
	if (judge(i, j, whoseTurn))
	{
		if (1 == whoseTurn % 2)
			printf("Winner is player 1!\n");
		else
			printf("Winner is player 2!\n");
	}
}

judge Function

Function parameters:

  • x: the row number of the current move
  • y: the column number of the current move

Return value:

  • 1 or 0. 1 means that the current player has won after making the move.
int judge(int x, int y)
{
    int i, j;
    int t = 2 - whoseTurn % 2;
    const int step[4][2]={{-1,0},{0,-1},{1,1},{1,0}};
    for(int i=0;i<4;++i)
    {
        const int d[2]={-1,1};
        int count=1;
        for(int j=0;j<2;++j)
            for(int k=1;k<=4;++k){
                int row=x+k*d[j]*step[i][0];
                int col=y+k*d[j]*step[i][1];
                if( row>=1 && row<=N && col>=1 && col<=N &&
                    chessboard[x][y]==chessboard[row][col])
                    count+=1;
                else
                    break;
            }
        if(count>=5)
            return 1;
    }
    return 0;
}

In the judge function, there are 3 nested for loops, and their purpose is to determine if there is a line of five consecutive pieces.

A line of five pieces can either be in a row, column, or diagonal direction. Here, we will use a trial-and-error approach, searching for consecutive pieces along the horizontal, vertical, and diagonal directions. Let's take an example:

Gomoku Game

In the above chessboard, we will explain the algorithm for determining if there is a line of five pieces based on the coordinates (9, 10).

First, we check if there is a diagonal line of five pieces starting from (9, 10). The process is as follows:

  • Starting from (9, 10), we search in the top-left direction. The coordinates that satisfy the condition are (8, 9), (7, 8), and (6, 7). Since (5, 6) does not satisfy the condition, we move on to the next step.
  • Then, we search in the bottom-right direction and find (10, 11), which is the only coordinate that satisfies the condition.
  • We have found five points in a straight line, so player 2 wins.

If there is no line of five pieces in the diagonal direction, we then check the vertical and horizontal directions. If none of them satisfy the winning condition, it means that the current player cannot win, and the game will continue.

My Chess Piece was "Eaten"

I don't know if anyone noticed, but in our Five in a Row game, even if a position is already occupied, we can still "eat" the original chess piece when placing our own.

This is because when we wrote the playChess function, we didn't check the position where we placed the chess piece. We can modify our code like this:

void playChess(void)
{
	int i, j, winner;
	if (1 == whoseTurn % 2)
	{
		printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
		scanf("%d %d", &i, &j);
		//debug
		while(chessboard[i][j] != 0)
		{
			printf("This position has been occupied, please input the position again: ");
			scanf("%d %d",&i, &j);
		}
		//debug
		chessboard[i][j] = 1;
	}
	else
	{
		printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
		scanf("%d %d", &i, &j);
		//debug
		while(chessboard[i][j] != 0)
		{
			printf("This position has been occupied, please input the position again: ");
			scanf("%d %d",&i, &j);
		}
		//debug
		chessboard[i][j] = 2;
	}

	system("clear");
	printChessboard();

	if (judge(i, j))
	{
		if (1 == whoseTurn % 2)
			printf("The winner is player 1!\n");
		else
			printf("The winner is player 2!\n");
	}
}

We added a loop in the function, so when the position is already occupied, it gives a prompt and asks for a new input.

Why can't I Win Forever?

When there is a line of five pieces, and it prompts "Player 1" or "Player 2" wins, it then prompts "It's player *'s turn, please enter the position of your piece...". Isn't it frustrating? Actually, all we need to do is add one line of code!

	if (judge(i, j))
	{
		if (1 == whoseTurn % 2)
		{
			printf("Winner is player 1!\n");
			exit(0);	//debug
		}
		else
		{
			printf("Winner is player 2!\n");
			exit(0);	//debug
		}
	}
}

In addition to prompting the player who wins after winning, exit the game with exit(0).

Compilation and Running

Execute the gcc command to compile:

cd ~/project
gcc -o gomoku gomoku.c
./gomoku
Gomoku Game

Summary

Congratulations! You have created a simple Gomoku game using C language. Players can take turns placing their pieces on a 15x15 board, and when a player has five consecutive pieces, the program will declare them the winner. Enjoy playing this text-based game with your friends!

Other C Tutorials you may like