C語言小項目--《三子棋》實戰訓練

寫在開頭本案例代碼採用win10系統下 Visual Studio 2019 編譯器進行書寫編譯的。對於該編譯器「scanf()」編譯時沒法經過,解決方案在每一個須要該函數的文件的第一行加入以下代碼:數組

#define _CRT_SECURE_NO_WARNINGS 1

    首先創建主程序文件main.c,函數文件game.c,頭文件game.h。其中函數文件用於存放對遊戲中各個部分功能實現的函數代碼;頭文件用於進行函數聲明。
ide

    對於任何程序,都須要一個主體部分,在三子棋遊戲中也不例外。對於一個遊戲,基本包括遊戲界面和遊戲選項。遊戲運行,首先進行遊戲菜單打印,等待用戶輸入,根據用戶輸入內容進行下一步的操做。其中菜單部分可使用menu()函數進行實現,用戶輸入則用scanf()函數來接收。規定:菜單打印兩個選項,當用戶鍵入1時,則開始進行遊戲(遊戲部分由game()函數實現);當用戶鍵入0時,則退出程序;若用戶鍵入其餘字符,則提示用戶輸入有誤,需從新出入。所以可使用switch()函數來實現此部分功能。具體實現代碼以下:函數

int main(void)
{
	int input;
	srand((unsigned int)time(NULL));
	printf("****三子棋遊戲****\n");
	printf("玩家執子:*;電腦執子:#\n");
	do
	{
		menu();
		printf("請選擇:>");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("遊戲結束。\n");
				break;
			default:
				printf("輸入錯誤,請從新輸入!!!\n\n");
				break;			
		}
	} while (input);
	return 0;
}


其中menu()函數的代碼以下:
spa

//打印菜單
void menu(void)
{
	printf("*************************\n");
	printf("**1.play*********0.exit**\n");
	printf("*************************\n");
}

    對於遊戲部分,首先是要生成棋盤,每次進入遊戲初始都要對棋盤內容進行初始化,而後對棋盤進行可視化。生成棋盤可使用一個2維數組進行,只需對玩家和電腦每次輸入的座標進行存放。初始化棋盤使用InitBoard()函數進行,可視化用DisplayBoard()函數進行。3d

    其次是遊戲的主要部分,生成棋盤後,玩家和電腦分別落子,當有一方贏或者平局則退出遊戲。玩家落子使用PlayerMove()函數實現,電腦落子使用ComMove()函數實現,判斷輸贏則用Winner()函數實現。下邊則對上述的各個函數功能的邏輯進行分析並實現。code

棋盤部分
blog

    爲了保證程序的健壯性,便可以方便改變棋盤的大小,在頭文件中定義三個常量來存放棋盤的大小和玩的大小(如能夠玩4子棋,5子棋等)。注意定義常量時最後不要加「;」,不然後邊代碼會出錯。遊戲

#define COL 5 //棋盤有5列
#define ROW 5 //棋盤有5行
#define COUNT 3 //三子棋

    建立棋盤:
input

//建立一個棋盤
char board[ROW][COL] = { 0 };

    對棋盤進行初始化:每開一次新的遊戲後都須要對棋盤進行初始化。對棋盤進行初始化即遍歷棋盤,把棋盤每一個位置元素都置空。所以該函數須要可以接收棋盤而且知道棋盤的大小。其代碼對應以下:編譯器

//初始化棋盤
void InitBoard(char board[ROW][COL],int  row, int col)
{
	int i;
	int j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

    棋盤可視化:在屏幕上打印棋盤;棋盤上應該包含位置座標,以便於用戶輸入。棋盤以下圖所示:

image.png

    能夠看出,首先在屏幕上打印出橫座標(或者最後打印出來),而後每一行均可以當作是由如下兩部分字符組成「(空格)(數組內容)(空格)|」,每一行的最後一列沒有「|」,緊接着是「---|」,同理最後一列沒有「|」。代碼實現以下:

//顯示棋盤
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i;
	int j;
	//首先打印出橫座標
	printf("     ");
	for (j = 0; j < col; j++)
	{
		printf("%2d  ",j+1);
	}
	printf("\n\n");
	
	//打印棋盤
	for (i = 0; i < row; i++)
	{
	        //棋盤的每一列開始打印縱座標
		printf("%3d  ",i+1);
		
		//打印棋盤每一行的第一部份內容
		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 < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

    遊戲部分

    生成棋盤後則玩家開始落子:

    玩家落子:要求函數能夠接收玩家輸入的座標,而且每次對玩家輸入的座標進行判斷,若是玩家輸入的位置上已經有棋子,則提示玩家已經有棋子,而且從新輸入,同理若是玩家輸入的座標範圍超出了棋盤範圍,應該提示玩家輸入座標不在棋盤範圍內,從新輸入座標,此外,棋盤顯示的座標位置是從1開始,而二維數組下標是從0開始,函數內部還要對座標進行轉換。

    電腦落子:電腦使用隨機數進行落子,同玩家落子同樣,須要進行一樣判斷,但不須要進行提示。此外,電腦進行落子後,須要對其座標進行記錄並輸出,提示玩家電腦落子的位置(若是棋盤過大,落子過多,不提示則不清楚電腦的落子位置),同理,函數也需對座標進行轉化。C語言中函數沒法返回兩個值,因此須要用一個長度爲2的整型數組對座標進行存放,以便後續屏幕輸出和判斷輸贏使用。


    玩家落子函數代碼以下:

//玩家落子,函數形參中arr用於接收落子座標
void PlayerMove(char board[ROW][COL], int row, int col, int arr[])
{
	int i;
	int j;

	printf("該玩家落子\n");
	printf("請輸入位置:");
	printf("例如:1 1(該位置對應棋盤左上角第一格,輸入完畢按enter鍵結束)\n");
	while (1)
	{
	        //接收座標
		scanf("%d %d", &i, &j);
		
		//將座標轉換爲棋盤對應的數組下標並對輸入座標的合法性進行判斷
		//座標範圍判斷
		if ((i - 1) >= 0 && (i - 1) < row && (j - 1) >= 0 && (j - 1) < row )
		{
		    //輸入座標內容判斷,若是沒有棋子則落子
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = '*';
				arr[0] = i-1;      //存放落子橫座標對應的數組下標
				arr[1] = j-1;      //存放落子縱座標對應的數組下標
				return arr;         //將下標進行返回
			}
			else
			{
				printf("該位置已有棋子,請從新輸入!!\n");
			}
		}
		else 
		{
			printf("輸入座標不在棋盤範圍內,請從新輸入!!\n");
		}
	}
}

    電腦落子代碼以下:

//電腦落子
void ComMove(char board[ROW][COL], int row, int col,int arr[])
{
	printf("該電腦落子:\n");
	int i;
	int j;
	while (1)
	{
		i = rand() % row;  //在0-棋盤橫向長度的範圍內生成隨機數
		j = rand() % col;  //在0-棋盤縱向長度的範圍內生成隨機數
		if (board[i][j ] == ' ')
		{
			board[i][j] = '#';
			arr[0] = i;
			arr[1] = j;
			return arr; //將電腦該步的位置返回
		}
	}
}

輸贏判斷

    每次玩家或電腦進行落子後,都須要進行判斷輸贏。以三子棋(頭文件中COUNT 設爲3)爲例,落子後以該子爲起點,在其橫向、縱向、斜向進行搜索,若是有三個連續相同的子,則提示本輪落子的獲勝,同時退出遊戲。若是棋盤最後一個位置落子後仍未有贏家,則提示平局,並退出遊戲。

    由上邊落子代碼看出,落子時返回了落子時的座標,該座標在此處進行傳參。

    以橫向搜索爲例:先向右進行搜索,使用一個計數器接收相同棋子的個數,若是碰到相同的棋子則計數器加一,碰到不一樣的棋子(棋盤爲空也算不一樣的棋子)則直接退出搜索;而後向左進行一樣的步驟。搜索時要注意不要超出棋盤範圍,能夠看出,橫向搜索時只有橫座標發生變化,而縱座標沒有發生變化。

    其餘三個方向搜索同理(斜向有兩個)。總共有四個方向,因此使用一個長度爲4的一維數組來接收四個方向的計數器值。

    若是任意一個方向的棋子數大於或者等於COUNT,則該函數返回落子位置對應的子(這裏就是字符:*或#)。

    若是該次落子後尚未贏家,則判斷棋盤是否下滿,若是沒有下滿,則函數返回「C」(Continue,表示繼續下一回合),若是棋盤下滿,則函數返回「E」(End,表示遊戲結束)。


    判斷輸贏代碼以下:

//判斷輸贏
char Winner(char board[ROW][COL], int row, int col,int arr[])
{
	int i;
	int j;
	int x = arr[0]; //落子橫座標對應棋盤數組的下標
	int y = arr[1]; //落子縱座標對應棋盤數組的下標
	int count[4] = {1,1,1,1}; //用於接收四個方向連續子長度的數組

	//向左檢查
	//縱座標不變,橫座標增長
	for (i = x+1; i < row; i++)
	{
		if (board[i][y] == board[x][y] && board[x][y] != ' ')
		{
			(count[0])++;
		}
		else
		{
			break;
		}
	}
	//向右檢查
	for (i = x-1; i >= 0; i--)
	{
		if (board[i][y] == board[x][y] && board[x][y] != ' ')
		{
			(count[0])++;
		}
		else
		{
			break;
		}
	}

	//向上檢查
	for (j = y+1; j < col; j++)
	{
		if (board[x][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[1])++;
		}
		else
		{
			break;
		}
	}

	//向下檢查
	for (j = y-1; j >= 0; j--)
	{
		if (board[x][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[1])++;
		}
		else
		{
			break;
		}
	}

	//向右上檢查
	i = x + 1;
	j = y + 1;
	while (i < row && j < col)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[2])++;
			i++;
			j++;
		}
		else
		{
			break;
		}
	}
	//向左下檢查
	i = x - 1;
	j = y - 1;
	while (i >= 0 && j >= 0)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[2])++;
			i--;
			j--;
		}
		else
		{
			break;
		}
	}

	//向左上檢查
	i = x - 1;
	j = y + 1;
	while (i >= 0 && j < col)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[3])++;
			i--;
			j++;
		}
		else
		{
			break;
		}
	}

	//向右下檢查
	i = x + 1;
	j = y - 1;
	while (i < row && j >= 0)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[3])++;
			i++;
			j--;
		}
		else
		{
			break;
		}
	}
        
        //任意方向的計數器超過COUNT,就返回落子時對應的子
	for (i = 0; i < 4; i++)
	{
		if (count[i] >= COUNT)
		{
			return board[x][y];
		}
	}

	//判斷棋盤是否下滿
	int ret = IsFull(board, ROW, COL);
	if (ret)
	{
		return 'E';
	}
	return 'C';
}

    判斷棋盤是否下滿的函數:對棋盤進行遍歷,若是每一個位置都不爲空,就返回1;不然返回0.

//判斷棋盤是否落滿子
int IsFull(char board[ROW][COL], int row, int col)
{
	int i;
	int j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j< row; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

game()實現

    上邊對該遊戲描述的每一個功能都進行了實現,下邊在game()函數裏對這些函數進行組裝

//遊戲主體
void game(void)
{
	char ret;

	//用於接收落子座標
	int arr[2];

	//遊戲界面部分
	//建立一個棋盤
	char board[ROW][COL] = { 0 };
	//初始化棋盤
	InitBoard(board, ROW, COL);
	//打印棋盤
	DisplayBoard(board, ROW, COL);

	//遊戲功能部分
	while (1)
	{
		//玩家落子
		PlayerMove(board, ROW, COL,arr);
		DisplayBoard(board, ROW, COL);
		\\落子後進行判斷,由Winner()函數返回值能夠看出,只要返回值是C就繼續進行遊戲
		ret = Winner(board, ROW, COL,arr);
		if (ret !='C')
		{
			break;
		}
		//電腦落子
		ComMove(board, ROW, COL,arr);
		printf("電腦落子位置是:(%d,%d)\n",arr[0]+1,arr[1]+1);
		DisplayBoard(board, ROW, COL,arr);
		ret = Winner(board, ROW, COL,arr);
		if (ret != 'C')
		{
			break;
		}
	}
	
	
	if (ret == '*')
	{
		printf("恭喜你,勝利!\n");
	}
	else if (ret == '#')
	{
		printf("很遺憾,失敗了!\n");
	}
	else
	{
		printf("旗鼓至關,打成平局\n");
	}
}


總結

    main.c文件的代碼以下:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

int main(void)
{
	int input;
	srand((unsigned int)time(NULL));
	printf("****三子棋遊戲****\n");
	printf("玩家執子:*;電腦執子:#\n");
	do
	{
		menu();
		printf("請選擇:>");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("遊戲結束。\n");
				break;
			default:
				printf("輸入錯誤,請從新輸入!!!\n\n");
				break;			
		}
	} while (input);
	return 0;
}


    game.c文件的代碼以下:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//打印菜單
void menu(void)
{
	printf("*************************\n");
	printf("**1.play*********0.exit**\n");
	printf("*************************\n");
}

//初始化棋盤
void InitBoard(char board[ROW][COL],int  row, int col)
{
	int i;
	int j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

//顯示棋盤
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i;
	int j;
	printf("     ");
	for (j = 0; j < col; j++)
	{
		printf("%2d  ",j+1);
	}
	printf("\n\n");
	for (i = 0; i < row; i++)
	{
		printf("%3d  ",i+1);
		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 < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

//玩家落子
void PlayerMove(char board[ROW][COL], int row, int col, int arr[])
{
	int i;
	int j;

	printf("該玩家落子\n");
	printf("請輸入位置:");
	printf("例如:1 1(該位置對應棋盤左上角第一格,輸入完畢按enter鍵結束)\n");
	while (1)
	{
		scanf("%d %d", &i, &j);
		if ((i - 1) >= 0 && (i - 1) < row && (j - 1) >= 0 && (j - 1) < row )
		{
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = '*';
				arr[0] = i-1;
				arr[1] = j-1;
				return arr;
			}
			else
			{
				printf("該位置已有棋子,請從新輸入!!\n");
			}
		}
		else 
		{
			printf("輸入座標不在棋盤範圍內,請從新輸入!!\n");
		}
	}
}
//電腦落子
void ComMove(char board[ROW][COL], int row, int col,int arr[])
{
	printf("該電腦落子:\n");
	int i;
	int j;
	while (1)
	{
		i = rand() % row;
		j = rand() % col;
		if (board[i][j ] == ' ')
		{
			board[i][j] = '#';
			arr[0] = i;
			arr[1] = j;
			return arr;
		}
	}
}

//判斷輸贏
char Winner(char board[ROW][COL], int row, int col,int arr[])
{
	int i;
	int j;
	int x = arr[0];
	int y = arr[1];
	int count[4] = {1,1,1,1};

	//向左檢查
	for (i = x+1; i < row; i++)
	{
		if (board[i][y] == board[x][y] && board[x][y] != ' ')
		{
			(count[0])++;
		}
		else
		{
			break;
		}
	}
	//向右檢查
	for (i = x-1; i >= 0; i--)
	{
		if (board[i][y] == board[x][y] && board[x][y] != ' ')
		{
			(count[0])++;
		}
		else
		{
			break;
		}
	}

	//向上檢查
	for (j = y+1; j < col; j++)
	{
		if (board[x][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[1])++;
		}
		else
		{
			break;
		}
	}

	//向下檢查
	for (j = y-1; j >= 0; j--)
	{
		if (board[x][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[1])++;
		}
		else
		{
			break;
		}
	}

	//向右上檢查
	i = x + 1;
	j = y + 1;
	while (i < row && j < col)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[2])++;
			i++;
			j++;
		}
		else
		{
			break;
		}
	}
	//向左下檢查
	i = x - 1;
	j = y - 1;
	while (i >= 0 && j >= 0)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[2])++;
			i--;
			j--;
		}
		else
		{
			break;
		}
	}

	//向左上檢查
	i = x - 1;
	j = y + 1;
	while (i >= 0 && j < col)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[3])++;
			i--;
			j++;
		}
		else
		{
			break;
		}
	}

	//向右下檢查
	i = x + 1;
	j = y - 1;
	while (i < row && j >= 0)
	{
		if (board[i][j] == board[x][y] && board[x][y] != ' ')
		{
			(count[3])++;
			i++;
			j--;
		}
		else
		{
			break;
		}
	}

	for (i = 0; i < 4; i++)
	{
		if (count[i] >= COUNT)
		{
			return board[x][y];
		}
	}

	//棋盤是否滿?
	int ret = IsFull(board, ROW, COL);
	if (ret)
	{
		return 'E';
	}
	return 'C';
}

//判斷棋盤是否落滿子
int IsFull(char board[ROW][COL], int row, int col)
{
	int i;
	int j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j< row; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}



//遊戲主體
void game(void)
{
	char ret;

	//用於接收落子座標
	int arr[2];

	//遊戲界面部分
	//建立一個棋盤
	char board[ROW][COL] = { 0 };
	//初始化棋盤
	InitBoard(board, ROW, COL);
	//打印棋盤
	DisplayBoard(board, ROW, COL);

	//遊戲功能部分
	while (1)
	{
		//玩家落子
		PlayerMove(board, ROW, COL,arr);
		DisplayBoard(board, ROW, COL);
		ret = Winner(board, ROW, COL,arr);
		if (ret !='C')
		{
			break;
		}
		//電腦落子
		ComMove(board, ROW, COL,arr);
		printf("電腦落子位置是:(%d,%d)\n",arr[0]+1,arr[1]+1);
		DisplayBoard(board, ROW, COL,arr);
		ret = Winner(board, ROW, COL,arr);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("恭喜你,勝利!\n");
	}
	else if (ret == '#')
	{
		printf("很遺憾,失敗了!\n");
	}
	else
	{
		printf("旗鼓至關,打成平局\n");
	}
}

    game.h頭文件代碼以下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#ifndef __GAME_H__
#define __GAME_H__

#define COL 5
#define ROW 5
#define COUNT 3

void menu(void);
void game(void);
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col,int arr[]);
void ComMove(char board[ROW][COL], int row, int col,int arr[]);
char Winner(char board[ROW][COL], int row, int col,int arr[]);
int IsFull(char board[ROW][COL], int row, int col);
#endif
相關文章
相關標籤/搜索