【C语言】小游戏--三子棋

目录

事先准备:

头文件的作用:

使用宏定义:

基本思路:

菜单界面

测试的逻辑部分:

1.初始化棋盘为全空格

2.打印棋盘

3.玩家下棋

4.电脑下棋

扫描二维码关注公众号,回复: 15255653 查看本文章

5.判断棋盘满的条件

判断平局条件 

游戏继续 

6.判断胜利条件

玩家胜利条件:

电脑胜利条件: 


事先准备:

三子棋的玩法如下:

我们在写一些重要的代码时,最好把自定义函数的声明、定义以及整个程序的实现,分别放在不同的文件里面。这样写不仅会让你的逻辑更清晰、更容易调试代码还便于后续对整个代码的保密操作。

游戏的实现逻辑: 

  1. test.c      ——   测试游戏
  2. game.h   ——  游戏函数的声明
  3. game.c   ——  游戏函数的实现

头文件的作用:

#include<stdlib.h>

#include<time.h>


#include<stdio.h>

使用宏定义:

#define ROW 3
#define COL 3

 使用宏定义的原因:
1.推高代码可读性,后续代码中直接用变量代替,参数修改量大大减少。
2.提高扩展性,如果将来要修改棋盘尺寸,代码修改会很方便。
 

基本思路:

1.菜单界面选择/退出游戏

2.创建棋盘并初始化(棋盘为全空格)
    char board[ROW][COL];//存放玩家或者电脑的落子情况
    char ret = 0;

3.打印棋盘

循环{

玩家下棋

打印棋盘

判断玩家输赢:*

电脑下棋

打印棋盘

判断电脑输赢:#

}

【平局:Q(特殊情况)游戏继续:C】

以此循环第1条功能

菜单界面

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

测试的逻辑部分:

void game()
{
	//存放玩家或者电脑的落子情况
	char board[ROW][COL];
	char ret = 0;
    // 初始化一下棋盘为全空格
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
	while (1) {
		//玩家下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		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");
        print_board(board, ROW, COL);
}
void test()
{	 
	srand((unsigned int)time(NULL));
	int input=0;
	do {
		menu();
		printf("请选择:>");
        scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

 这部分代码之所以放外面是为了避免代码冗余

注意:

 srand((unsigned int)time());srand()函数需要一个time函数的返回值作为参数,但time()函数的返回值是time_t,srand又需要unsigned int类型。

1.初始化棋盘为全空格

游戏开始之前要进行初始化,先建立一个空的棋盘雏形,这里用二维数组来替代棋盘上的位置,空格符号来代表此时棋盘上没有棋子落子。此时就是一个空棋盘,每次开始之前都要置空。 

使用“InitBoard()”函数来进行棋盘的初始化操作。

char board[ROW][COL];
init_board(board, ROW, COL);
void init_board(char board[ROW][COL], int row, int 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] = ' ';
		}
	}
}

2.打印棋盘

打印效果:

可以看出上面打印的棋盘是有归律的

_ _ _ | _ _ _ | _ _ _ 

这是它的规律,而第3列是没有|,所以当j<col-1时才打印|

可看到最后一行没有_ _ _可说明当i<row-1时才打印_ _ _

 测试和头文件部分: 

print_board(board, ROW, COL);
void print_board(char board[ROW][COL],int row, int col);

 代码实现: 

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

3.玩家下棋

三点基本要求

  1. 接收玩家所输入的地址位置(我们所判断的数组元素应该是玩家输入值-1
  2. 判断玩家所输入的地址所在是否已有落子
  3. 将玩家的棋子落入对应位置
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);

这样写会把棋盘写死 

还有就是不方便扩展棋盘,假设变成5x5的棋盘修改量巨大

测试和头文件部分: 

player_move(board, ROW, COL);
void player_move(char board[ROW][COL], int row,int col);

代码实现: 

void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	while (1) {
		printf("请输入要下棋的坐标:>");
		int x = 0, y = 0;
		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("该坐标已被占用,请重新输入\n");
			}
		}
		else {
			printf("坐标越界\n");
		}
	}

}

运行结果:

4.电脑下棋

随机生存坐标,只要坐标没被占用,就下棋

srand()函数只用调用一次即可


  int x = rand() % row;
  int y = rand() % col;

 rand()函数生成的自然数是0-32767之间的数,若rand()%10那么就是0-9之间的数

也需要判断电脑所下的地址是否被占用,以及将棋子落下,这点和玩家所需的要求一致

测试和头文件部分: 

computer_move(board,ROW,COL);
void computer_move(char board[ROW][COL], int row, int col);

代码实现: 

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:\n");
	while (1)//反复下
	{
		int x = rand() % row;//0-2范围
		int y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

运行结果:

\

5.判断棋盘满的条件

使用is_full()函数来实现这一功能。

在这一函数中,需要实现以下几个逻辑:

判断游戏结束的四种状态:
玩家赢     --  返回'*'
电脑赢     --  返回'#'
平局         --  返回'Q'
游戏继续  --  返回'C'

前面的ret是用于返回的一个值,若ret != 'C' ,则游戏继续 ,用char ret = 0定义,

static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

判断平局条件 

  

is_full这个函数只是为了支持is_win函数的,只是在is_win函数内部使用,没必要在头文件中声明

运行结果:

游戏继续 

6.判断胜利条件

实现判断胜利的函数是is_win(), 前面玩家和电脑赢的为啥会返回一个'*'和'#', 以下举例解释:

 其实就是3种情况,就是判断行、列和对角线

测试和头文件部分: 

char board[ROW][COL];
char ret = 0;
ret = is_win(board, ROW, COL);

代码实现:  

char is_win(char board[ROW][COL], int row, int col)
{
	int  i = 0;
	//判断三行 
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0]!=' ')
		{
			return board[i][0];
		}
	}
	//判断三列
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
			return board[0][i];
	}
	//对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
	{
		return board[2][0];
	}
	//平局 
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}

玩家胜利条件:

电脑胜利条件: 

猜你喜欢

转载自blog.csdn.net/weixin_65186652/article/details/128904415