首頁 > 軟體

C語言三子棋的實現思路到過程詳解

2023-02-14 06:01:06

一、三子棋小遊戲的簡單介紹

要說大家都很熟悉的一個小遊戲,三子棋算是其中一個了。相信大家都玩過三子棋小遊戲,在這裡我還是給大家介紹簡單的遊戲規則:

  • 一次只能下一個棋子;
  • 玩家下完棋子後,電腦下棋子;
  • 不能再重複的位置上下棋子;
  • 不管是玩家還是電腦,誰先達到三個棋子連線在一起的時候獲勝;
  • 三個棋子的連線包括:橫三個、豎三個、斜三個。

當然,熟悉規則後我們會有一個大概的瞭解了,那麼三子棋遊戲的思路及程式碼到底是怎麼實現的呢?接下來我給大家一一詳細解析一下。

二、三子棋的思路及程式碼實現

1、列印遊戲選單

我們實現遊戲之前,應該想到先給玩家提供一個選單。這個選單的功能就是幫助使用者選擇是否要開始遊戲。選單的實現我們可以單獨自定義一個函數,我們用到選單的時候呼叫此函數即可。

void meau()
{
	printf("*********************n");
	printf("*****  1.play   *****n");
	printf("*****  0.exit   *****n");
	printf("*********************n");
}

通過上面的程式碼我們可以很容易看出,選擇‘1’是開始遊戲,選擇‘0’是退出遊戲。

2、選擇是否開始遊戲

提到選擇,我們這裡可以聯想到switch-case語句。由上面的選單可知:選擇‘1’是開始遊戲,選擇‘0’是退出遊戲。當然我們不能排除不小心輸入錯誤,所以這裡我們還要考慮到選擇錯誤的情況下要給出相應的提示。當選擇錯誤時,給出提示且重新選擇,同時再把選單列印出,提供玩家選擇。那怎麼實現重新選擇呢?我們這裡其實可以使用do-while()語句。我們先來看一下程式碼的實現。

void test()
{
	int input = 0;
	do
	{
		meau();
		printf("請選擇是否要開始遊戲:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();  //開始三子棋遊戲
			break;
		case 0:
			printf("退出遊戲n");
			break;
		default:
			printf("選擇錯誤n");
		}
	} while (input);
}

我們看上面的程式碼是放在的一個自定義的test()函數中,我們只要再主函數中呼叫一下即可。上面的程式碼很巧妙,當我們輸入‘1’的時候,開始遊戲。當遊戲結束時,迴圈繼續。其實是實現了一個玩完一局可以反覆玩的效果。當我們輸入‘0’的時候,迴圈結束,就是相當於結束遊戲了。當我們輸入錯誤時,迴圈仍然繼續,再次列印選單提供我們選擇。這也是 do-while()語句的巧妙之處。

3、建立並且初始化棋盤

3.1、建立棋盤

建立棋盤很簡單,我們這裡需要的是一個二維陣列。那麼棋盤的大小呢?我們首先想到的是3x3的棋盤——char board[3][3]。那要是想改成5x5的棋盤呢?我們是把整個工程中的board[3][3]改成board[5][5]嗎?這樣太麻煩了,當然也不現實。這裡我們可以參照#define 定義的識別符號常數。這時候我們可以寫成char board[ROW][COL]。改變大小的時候只需要改變#define 定義的識別符號常數的值就行。

#define ROW 3
#define COL 3
char board[ROW][COL];

3.2、初始化棋盤

我們這裡將初始化棋盤放在一個init_board(board, ROW, COL)函數中。為什麼要初始化棋盤呢?當我們不初始化的時候,期盼中的每個位置放的是‘’。而我們想要得到的棋盤是一個空棋盤,這樣的話更加有利於玩家操作下棋。空棋盤看起開也比較整潔。我們看一下初始化程式碼的實現。

void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

4、列印格式化棋盤

當我們初始化完棋盤後,我們就應該把棋盤列印出來讓玩家看到並且選擇要下棋的位置。這裡我們先來看列印棋盤。列印出來的棋盤應該格式鮮明,每個位置獨立分開,而不是一片空白。我們先看一下棋盤的格式:

3x3

5x5

通過上面的兩個圖,我們就可以建立一個大概的列印棋盤的思路了。其實我們可以把”_ _ _|_ _ _|_ _ _“看作我們要列印的第一行內容,但是要注意最後一行是” | | “。列印的思路有了,把列印棋盤內容放在print_board(board, ROW, COL)函數中。我們來看一下程式碼的實現。

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("n");
		if (i < row - 1)
		{
			for (j = 0; j < row; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("n");
	}
}

5、玩家下棋

當我們把棋盤列印出來後,這時候就要提示玩家選擇下棋了。我們採用的是座標的形式讓玩家進行選擇下棋位置。這裡要有幾點要注意的事項

  • 玩家選擇的位置就是所看到的位置,跟程式碼中的陣列下標存取還是有所差距的;
  • 玩家輸入的座標後,要判斷該座標是否已經被佔用,也就是不能重複在同一個位置上下棋;
  • 玩家輸入座標後,要判斷座標是否合法,不合法的話要給出提示,並且重新輸入。
  • 當玩家輸入的座標合法後,電腦玩家進行下棋;
  • 玩家下完棋後要再次呼叫列印棋盤函數print_board(board, ROW, COL),使玩家更方便的觀看已經下棋的位置;
  • 我們把玩家下的座標用 ‘ * ’ 來代表。

我們將玩家下棋內容放在player_move(board, ROW, COL)函數中,我們來看一下玩家下棋的程式碼實現。

void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("請選擇你要下棋的座標:");
	while (1)
	{
		scanf("%d %d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("該座標已經被佔有,請重新選擇:");
			}
		}
		else
		{
			printf("該座標非法,請輸入合法座標:");
		}
	}
}

6、電腦下棋

玩家下棋後,就該電腦下棋了。電腦下棋其實就是隨機下棋。當然電腦下棋也是不能重複在同一個位置上下棋,且是合法的。提到隨機,我們就因該聯想到rand()函數和srand()函數,在這裡我就不詳細介紹這兩個函數的使用方法了,在之前的猜數位小遊戲中有詳細的解釋,可以去了解一下。電腦下完棋後也要呼叫列印棋盤函數print_board(board, ROW, COL),使玩家更方便的觀看已經下棋的位置。我們把玩家下的座標用 ‘ #’ 來代表。把電腦下棋程式碼放在computer_move(board, ROW, COL)函數中。那我們來看一下電腦下棋的程式碼實現。

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("電腦下棋:n");
	while (1)
	{
		int x = rand() % 3;
		int y = rand() % 3;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

7、判斷是否玩家或者電腦贏

其實,每當玩家或者電腦下完一次棋後,我們都需要判斷一下是否有贏的。如果沒有贏的,我們就進行反覆下棋。如果有贏的,我們就停止下棋,並輸出”玩家贏“或者”電腦贏“。我們同時還要想到是否為平局,如果為平局的話,就輸出”平局“。判斷輸贏的函數我們定義成char is_win(board[ROW][COL], ROW, COL)。

判斷輸贏函數返回值注意:

  • 我們這個判斷輸贏的函數是有返回值的,返回型別為char;
  • 當返回 ‘*’ 時,玩家勝利;
  • 當返回 ‘#’ 時,電腦勝利;
  • 當返回 ‘Q’ 時,平局;
  • 當返回 ‘C’ 時,遊戲繼續。

當我們在編寫輸贏函數時,我們要注意不能陣列越界存取。我們先來看一下判斷輸贏函數的實現。

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	//判斷行
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (j == 0)
			{
				if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]) && (board[i][1] != ' '))
					return board[i][0];
			}
			else if (j == 1)
			{
				if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]) && (board[i][1] != ' ')
					|| (board[i][1] == board[i][2]) && (board[i][2] == board[i][23]) && (board[i][1] != ' '))
					return board[i][1];
			}
			else if (j == col - 1)
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else if (j == col - 2)
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j - 1]) && (board[i][j] == board[i][j + 1]) && (board[i][j] != ' '))
				return board[i][j];
			}
			else
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j - 1]) && (board[i][j] == board[i][j + 1]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j + 1]) && (board[i][j + 1] == board[i][j + 2]) && (board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷列
	int j = 0;
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row; i++)
		{
			if (i == 0)
			{
				if ((board[0][j] == board[1][j]) && (board[1][j] == board[2][j]) && (board[1][j] != ' '))
					return board[0][j];
			}
			else if (i == 1)
			{
				if ((board[0][j] == board[1][j]) && (board[1][j] == board[2][j]) && (board[1][j] != ' ')
					|| (board[1][j] == board[2][j]) && (board[2][j] == board[3][j]) && (board[1][j] != ' '))
					return board[1][j];
			}
			else if (i == row - 1)
			{
				if ((board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else if (i == row - 2)
			{
				if ((board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i + 1][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else
			{
				if ((board[i][j] == board[i + 1][j]) && (board[i + 1][j] == board[i + 2][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i + 1][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷主對角線
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i<row-2&&j<col-2)
			{
				if((board[i][j] == board[i + 1][j + 1] && board[i][j] == board[i + 2][j + 2] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i>0&&i<row-1&&j>0&&j<col-1)
			{
				if ((board[i][j] == board[i + 1][j + 1] && board[i][j] == board[i - 1][j - 1] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i >1&&j>1)
			{
				if ((board[i][j] == board[i - 1][j - 1] && board[i][j] == board[i - 2][j - 2] && board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷次對角線
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i<row-2&&j>1)
			{
				if ((board[i][j] == board[i + 1][j - 1] && board[i][j] == board[i + 2][j - 2] && board[i][j] != ' '))
					return board[i][j];
			}
			if (j>0&&j<col-1&&i>0&&i<row-1)
			{
				if ((board[i][j] == board[i - 1][j + 1] && board[i][j] == board[i + 1][j - 1] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i>1&&j<col-2)
			{
				if ((board[i][j] == board[i - 1][j + 1] && board[i][j] == board[i - 2][j + 2] && board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷平局
	int flag = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				flag = 1;
		}
	}
	if (flag == 0)
		return 'Q';
	return 'C';
}

我們這裡再看一下反覆呼叫玩家下棋player_move(board, ROW, COL)函數和電腦下棋computer_move(board, ROW, COL)函數和列印棋盤函數print_board(board, ROW, COL)函數到終止迴圈的程式碼。反覆呼叫這幾個函數也就是實現了反覆下棋的效果。如果沒有贏的,我們就進行反覆下棋。如果有贏的或者平局,我們就停止下棋。我們來看程式碼實現。

	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判斷是否結束 
		// * 玩家勝利
		// # 電腦勝利
		// Q 平局
		// C 繼續遊戲
		ret=is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		//電腦下棋
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}

綜上就是我整個三子棋遊戲實現的思路了。總體來說還是比較簡單的。我們把上面的程式碼整合一下來看。

三、整合三子棋遊戲程式碼

由於程式碼量相對來說有一點多,所以我們就將函數的宣告的定義分開,這樣有利於提高程式碼的可讀性,同時會保持一個良好的思路,且方便編寫程式碼。

我們將函數的宣告放在單獨的一個game.h的標頭檔案,函數的實現放在一個單獨的game.c原始檔,函數的主方法及呼叫放在另一個單獨的test.c原始檔。

game.h

#include<stdio.h>
//陣列行和列的大小
#define ROW 3
#define COL 3
//初始化陣列 
void init_board(char board[ROW][COL],int row,int col);
//列印格式化陣列
void print_board(char board[ROW][COL], int row, int col);
//玩家下棋 *
void player_move(char board[ROW][COL], int row, int col);
//電腦下棋 #
void computer_move(char board[ROW][COL], int row, int col);
//判斷輸贏或者平局
char is_win(char board[ROW][COL], int row, int col);

game.c

#include "game.h"
void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("n");
		if (i < row - 1)
		{
			for (j = 0; j < row; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("n");
	}
}
void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("請選擇你要下棋的座標:");
	while (1)
	{
		scanf("%d %d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("該座標已經被佔有,請重新選擇:");
			}
		}
		else
		{
			printf("該座標非法,請輸入合法座標:");
		}
	}
}
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("電腦下棋:n");
	while (1)
	{
		int x = rand() % 3;
		int y = rand() % 3;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	//判斷行
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (j == 0)
			{
				if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]) && (board[i][1] != ' '))
					return board[i][0];
			}
			else if (j == 1)
			{
				if ((board[i][0] == board[i][1]) && (board[i][1] == board[i][2]) && (board[i][1] != ' ')
					|| (board[i][1] == board[i][2]) && (board[i][2] == board[i][23]) && (board[i][1] != ' '))
					return board[i][1];
			}
			else if (j == col - 1)
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else if (j == col - 2)
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j - 1]) && (board[i][j] == board[i][j + 1]) && (board[i][j] != ' '))
				return board[i][j];
			}
			else
			{
				if ((board[i][j] == board[i][j - 1]) && (board[i][j - 1] == board[i][j - 2]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j - 1]) && (board[i][j] == board[i][j + 1]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i][j + 1]) && (board[i][j + 1] == board[i][j + 2]) && (board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷列
	int j = 0;
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row; i++)
		{
			if (i == 0)
			{
				if ((board[0][j] == board[1][j]) && (board[1][j] == board[2][j]) && (board[1][j] != ' '))
					return board[0][j];
			}
			else if (i == 1)
			{
				if ((board[0][j] == board[1][j]) && (board[1][j] == board[2][j]) && (board[1][j] != ' ')
					|| (board[1][j] == board[2][j]) && (board[2][j] == board[3][j]) && (board[1][j] != ' '))
					return board[1][j];
			}
			else if (i == row - 1)
			{
				if ((board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else if (i == row - 2)
			{
				if ((board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i + 1][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
			else
			{
				if ((board[i][j] == board[i + 1][j]) && (board[i + 1][j] == board[i + 2][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i + 1][j]) && (board[i][j] != ' ')
					|| (board[i][j] == board[i - 1][j]) && (board[i - 1][j] == board[i - 2][j]) && (board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷主對角線
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i<row-2&&j<col-2)
			{
				if((board[i][j] == board[i + 1][j + 1] && board[i][j] == board[i + 2][j + 2] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i>0&&i<row-1&&j>0&&j<col-1)
			{
				if ((board[i][j] == board[i + 1][j + 1] && board[i][j] == board[i - 1][j - 1] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i >1&&j>1)
			{
				if ((board[i][j] == board[i - 1][j - 1] && board[i][j] == board[i - 2][j - 2] && board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷次對角線
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i<row-2&&j>1)
			{
				if ((board[i][j] == board[i + 1][j - 1] && board[i][j] == board[i + 2][j - 2] && board[i][j] != ' '))
					return board[i][j];
			}
			if (j>0&&j<col-1&&i>0&&i<row-1)
			{
				if ((board[i][j] == board[i - 1][j + 1] && board[i][j] == board[i + 1][j - 1] && board[i][j] != ' '))
					return board[i][j];
			}
			if (i>1&&j<col-2)
			{
				if ((board[i][j] == board[i - 1][j + 1] && board[i][j] == board[i - 2][j + 2] && board[i][j] != ' '))
					return board[i][j];
			}
		}
	}
	//判斷平局
	int flag = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				flag = 1;
		}
	}
	if (flag == 0)
		return 'Q';
	return 'C';
}

test.c

#include "game.h"
void game()
{
	char ret = 0;
	srand(time(NULL));
	char board[ROW][COL];
	//初始化陣列 全為空格
	init_board(board, ROW, COL);
	//列印格式化陣列
	print_board(board, ROW, COL);
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判斷是否結束 
		// * 玩家勝利
		// # 電腦勝利
		// Q 平局
		// C 繼續遊戲
		ret=is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		//電腦下棋
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("恭喜玩家取得勝利!n");
	else if (ret == '#')
		printf("電腦取得勝利。n");
	else if (ret == 'Q')
		printf("平局了哦。n");
}
void meau()
{
	printf("*********************n");
	printf("*****  1.play   *****n");
	printf("*****  0.exit   *****n");
	printf("*********************n");
}
void test()
{
	int input = 0;
	do
	{
		meau();
		printf("請選擇是否要開始遊戲:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出遊戲n");
			break;
		default:
			printf("選擇錯誤n");
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

到此這篇關於C語言三子棋的實現思路到過程詳解的文章就介紹到這了,更多相關C語言三子棋內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com