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
相关文章
相关标签/搜索