首页 > 其他分享 >C语言初阶习题【19】三子棋游戏

C语言初阶习题【19】三子棋游戏

时间:2025-01-04 20:58:34浏览次数:3  
标签:初阶 19 MAX chessBoard int printf 习题 COL ROW

1.实现三子棋游戏

2.思路

我们把游戏实现部分放在game.c和game.h中,把游戏的测试代码放到test.c中
main函数在test.c中。

2.1 test.c中

  1. 先写main 函数,在main函数中调用test函数。
int main()
{
	test();
	return 0;
}
  1. test.c函数实现让玩家进行选择是否要进行游戏
    这里用到了do…while语句,先上来打印一个菜单,让玩家进行输入,根据玩家输入进入不同的分支。
void test()
{
	int input = 0;
	
	do
	{
		manu();
		printf("请输入你的选择:\n");
		scanf("%d", &input);
		switch(input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("您已退出游戏,欢迎下次再来!\n");
				break;
			default:
				printf("您的选择有误,请重新输入:\n");
		}
	} while (input);
}
  1. manu()函数很简单,直接print打印即可。和我们之前的猜数字游戏是一样的思路

void manu(void)
{
	printf("=====================\n");
	printf("=====1:开始游戏=====\n");
	printf("=====0:退出游戏=====\n");
	printf("=====================\n");
}

这里写完,可以把game()函数先屏蔽掉看看效果,输入其他数值直接报错让玩家继续输,输入1直接break,循环继续,输入0就直接退出了。

在这里插入图片描述

2.2 game()的实现

game()函数实现这部分是游戏流程。game()也是放在test.c中。但是涉及到game函数具体实现都会放到game.c中去。
我们先表示棋盘:创建一个 3*3的二维数组,每个元素是一个char类型,'X’表示玩家1、'O’表示玩家2、'空格’表示空白。 我在game.c函数实现中设计了如果是和局的话用‘h’表示
因为只要游戏没有结束,就要一直下棋,所以要用到循环while

  • 游戏流程:
    (1)创建棋盘,并初始化~把所有位置都设为空格
    (2)打印棋盘
    while(1)
    {
    (3)玩家进行落子,让玩家来输入一组坐标(row,col),进行落子
    (4)进行判定,有任何一方获胜或者平局就break;
    (5)电脑进行落子~ 电脑随机落子
    (6)进行判定,有任何一方获胜或者平局就break;
    }
    (7)判定是哪一方获胜或者平局了
  • 代码实现
void game()
{
	//0.创建棋盘
	char chessBoard[ROW_MAX][COL_MAX] = { 0 };


	//1.初始化棋盘
	init(chessBoard, ROW_MAX, COL_MAX);


	//2.打印棋盘
	print(chessBoard, ROW_MAX, COL_MAX);

	char ret = ' ';//这个需要定义在while之外
	while (1)
	{
		//3.玩家下棋
		palyer(chessBoard, ROW_MAX, COL_MAX);
		print(chessBoard, ROW_MAX, COL_MAX);
		ret = is_Win(chessBoard, ROW_MAX, COL_MAX);
		if ( ret!= ' ')
		{
			break;
		}

		//4.电脑下棋
		computer(chessBoard, ROW_MAX, COL_MAX);
		print(chessBoard, ROW_MAX, COL_MAX);
		ret = is_Win(chessBoard, ROW_MAX, COL_MAX);
		if (ret != ' ')
		{
			break;
		}
	}

		if (ret == 'X')
		{
			printf("玩家获胜\n");
		}else if (ret== 'O')
		{
			printf("电脑获胜\n");
		}
		else if(ret== 'h')
		{
			printf("平局\n");
		}
		
	}

2.3 game.c函数实现

我们注意game.c和game.h是配套使用的,game.c中的函数需要在game.h中进行声明。
在这里插入图片描述
在.c文件中需要引入我们自己写的.h头文件,用的是“ ”,一般引用库函数用的< >

#include "game.h"

2.3.1 棋盘初始化

init(chessBoard, ROW_MAX, COL_MAX);
最简单的写法就是使用for循环直接遍历,给每个元素初始化为空格,后面有学到了用memset函数,直接初始化。

memset函数的头文件是<string.h> 。这个的使用可以自己搜下,比较简单有三个参数,第一个是要初始化的元素地址,我们这里就是数组首元素,第二个参数是要初始化的值,我们这里是空格,第三个就是要初始化的长度,

//棋盘初始化
void init(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{
#if 0
	for (int row = 0; row < MAX_ROW; row++)
	{
		for (int col = 0; col < MAX_COL; col++)
		{
			chessBoard[row][col] = ' ';
		}
	}
#endif
	memset(chessBoard, ' ', ROW_MAX * COL_MAX * sizeof(char));
}

2.3.2 棋盘打印

void print(char chessBoard[ROW_MAX][COL_MAX], int row, int col)
思路:这个地方要考虑到打印我们的棋盘,否则全是空格就看不到任何输出。

void print(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)

{

		for (int row = 0; row < ROW_MAX; row++)
		{
			printf("+---+---+---+\n");
			printf("| %c | %c | %c |\n", chessBoard[row][0], chessBoard[row][1], chessBoard[row][2]);

		}
		printf("+---+---+---+\n");
}

2.3.2 玩家下棋

  • void palyer(char chessBoard[ROW_MAX][COL_MAX], int row, int col)
  • 思路:只要位置不为空就可以下棋,要注意限制玩家输入范围。
  • 代码实现
//玩家下棋
void palyer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{
	
	printf("玩家下棋:\n");
	int x = 0;
	int y = 0;


	while (1)
	{
		printf("请输入要下棋的坐标:x y\n");
		scanf("%d %d", &x, &y);
		if (x<0 || x>=ROW_MAX || y<0 || y>=COL_MAX)
		{
			printf("您的输入坐标有误请重新输入:\n");
			continue;
		}
		if (chessBoard[x][y] != ' ')
		{
			printf("您下的位置已经有棋子了。请重新输入:\n");
			continue;
		}
		else
		{
			chessBoard[x][y] = 'X';
			break;
		}
	}
}

2.3.3 电脑下棋

  • void computer(char chessBoard[ROW_MAX][COL_MAX], int row, int col)
  • 思路:电脑需要使用rand()函数生成随机数进行下棋,注意两个点,其一要限制输入0 1 2不能超过3,其二需要配合srand()函数一起使用,srand()需要用到时间戳time()函数,我们这个只需要调用一次放在了test函数中。这个在猜数字游戏里面也有实现过。rand函数的头文件是<stdlib.h> time函数的头文件是<time.h>
  • 代码实现
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL)); //在循环外面
	do
	{
		manu();
		printf("请输入你的选择:\n");
		scanf("%d", &input);
		switch(input)
		{
			case 1:
				//game();
				break;
			case 0:
				printf("您已退出游戏,欢迎下次再来!\n");
				break;
			default:
				printf("您的选择有误,请重新输入:\n");
		}
	} while (input);
}


//电脑下棋
void computer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{

	printf("电脑下棋:\n");
	while (1)
	{
		int a = rand() % ROW_MAX;
		int b = rand() % COL_MAX;
		if (chessBoard[a][b] != ' ')
		{
			continue;
		}
		chessBoard[a][b] = 'O';
		break;
	}
	
}

2.3.4 判断输赢

  • char is_Win(char chessBoard[ROW_MAX][COL_MAX], int row, int col)

  • 思路:这里之所以是char类型的返回值,是为了能够方便我们进行判断是谁赢了。判断的思路很简单,判断三行是否一致,三列是否一致,对角线是否一致,棋盘是否下满为和局,这里涉及到一个棋盘是否满的函数,这个函数我们只需要我们的game.c中使用,所以给添加了static,关闭它的外部属性,其他.c文件都不能调用我们这个函数。

  • 代码实现

static int is_Full(char chessBoard[ROW_MAX][COL_MAX])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW_MAX; i++)
	{
		for (j = 0; j < COL_MAX; j++)
		{
			if (chessBoard[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}


char is_Win(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{

	//判断三行
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (chessBoard[i][0] ==chessBoard[i][1] && chessBoard[i][1] == chessBoard[i][2]
			&& chessBoard[i][0] != ' ')
		{
		    	return chessBoard[i][0];
			
		}
	}

	//判断三列
	for (i = 0; i < col; i++)
	{
		if (chessBoard[0][i] == chessBoard[1][i] && chessBoard[1][i] == chessBoard[2][i]
			&& chessBoard[0][i] != ' ')
		{
			return chessBoard[0][i];
	
		}
	}

	//判断对角线

	if (chessBoard[0][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[2][2]
		&& chessBoard[0][0] != ' ')
	{
		return chessBoard[0][0];
	}
	if (chessBoard[2][0] == chessBoard[2][2] && chessBoard[2][2] == chessBoard[0][2]
		&& chessBoard[0][2] != ' ')
	{
		return chessBoard[0][2];
	}

	if (is_Full(chessBoard))
	{
		return 'h';//和局
	}
	

	return ' ';//游戏继续
	
}

我在写这部分的时候各种出错,有时候是少了一个等号,有时候是用错了循环变量,调试真的很重要,当发现自己的错误的时候有时候真的哭笑不得,还好有调试功能,可以让我一步一步跟着代码走排查自己的问题。

3.代码实现

  • test.c

#include "game.h"


void manu(void)
{
	printf("=====================\n");
	printf("=====1:开始游戏=====\n");
	printf("=====0:退出游戏=====\n");
	printf("=====================\n");
}

void game()
{
	//0.创建棋盘
	char chessBoard[ROW_MAX][COL_MAX] = { 0 };


	//1.初始化棋盘
	init(chessBoard, ROW_MAX, COL_MAX);


	//2.打印棋盘
	print(chessBoard, ROW_MAX, COL_MAX);

	char ret = ' ';
	while (1)
	{
		//3.玩家下棋
		palyer(chessBoard, ROW_MAX, COL_MAX);
		print(chessBoard, ROW_MAX, COL_MAX);
		ret = is_Win(chessBoard, ROW_MAX, COL_MAX);
		if ( ret!= ' ')
		{
			break;
		}

		//4.电脑下棋
		computer(chessBoard, ROW_MAX, COL_MAX);
		print(chessBoard, ROW_MAX, COL_MAX);
		ret = is_Win(chessBoard, ROW_MAX, COL_MAX);
		if (ret != ' ')
		{
			break;
		}
	}

		if (ret == 'X')
		{
			printf("玩家获胜\n");
		}else if (ret== 'O')
		{
			printf("电脑获胜\n");
		}
		else if(ret== 'h')
		{
			printf("平局\n");
		}
		
	}


void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		manu();
		printf("请输入你的选择:\n");
		scanf("%d", &input);
		switch(input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("您已退出游戏,欢迎下次再来!\n");
				break;
			default:
				printf("您的选择有误,请重新输入:\n");
		}
	} while (input);
}


int main()
{


	test();

}
  • game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

#define ROW_MAX 3
#define COL_MAX 3




//棋盘初始化
void init(char chessBoard[ROW_MAX][COL_MAX],int  row,int  col );

//棋盘打印
void print(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col);

//玩家下棋
void palyer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col);

//电脑下棋
void computer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col);

//判断输赢
char is_Win(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col);
  • game.c


#include "game.h"

//棋盘初始化
void init(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{
	memset(chessBoard, ' ', ROW_MAX * COL_MAX * sizeof(char));
}

//棋盘打印
void print(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)

{

		for (int row = 0; row < ROW_MAX; row++)
		{
			printf("+---+---+---+\n");
			printf("| %c | %c | %c |\n", chessBoard[row][0], chessBoard[row][1], chessBoard[row][2]);

		}
		printf("+---+---+---+\n");
}

//玩家下棋
void palyer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{
	
	printf("玩家下棋:\n");
	int x = 0;
	int y = 0;


	while (1)
	{
		printf("请输入要下棋的坐标:x y\n");
		scanf("%d %d", &x, &y);
		if (x<0 || x>=ROW_MAX || y<0 || y>=COL_MAX)
		{
			printf("您的输入坐标有误请重新输入:\n");
			continue;
		}
		if (chessBoard[x][y] != ' ')
		{
			printf("您下的位置已经有棋子了。请重新输入:\n");
			continue;
		}
		else
		{
			chessBoard[x][y] = 'X';
			break;
		}
	}
}


//电脑下棋
void computer(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{

	printf("电脑下棋:\n");
	while (1)
	{
		int a = rand() % ROW_MAX;
		int b = rand() % COL_MAX;
		if (chessBoard[a][b] != ' ')
		{
			continue;
		}
		chessBoard[a][b] = 'O';
		break;
	}
	
}


static int is_Full(char chessBoard[ROW_MAX][COL_MAX])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW_MAX; i++)
	{
		for (j = 0; j < COL_MAX; j++)
		{
			if (chessBoard[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

//判断输赢
//返回X表示玩家获胜
//返回O表示电脑获胜
//返回h表示和局
//返回j表示游戏继续

char is_Win(char chessBoard[ROW_MAX][COL_MAX], int  row, int  col)
{

	//判断三行
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (chessBoard[i][0] ==chessBoard[i][1] && chessBoard[i][1] == chessBoard[i][2]
			&& chessBoard[i][0] != ' ')
		{
		    	return chessBoard[i][0];
			
		}
	}

	//判断三列
	for (i = 0; i < col; i++)
	{
		if (chessBoard[0][i] == chessBoard[1][i] && chessBoard[1][i] == chessBoard[2][i]
			&& chessBoard[0][i] != ' ')
		{
			return chessBoard[0][i];
	
		}
	}

	//判断对角线

	if (chessBoard[0][0] == chessBoard[1][1] && chessBoard[1][1] == chessBoard[2][2]
		&& chessBoard[0][0] != ' ')
	{
		return chessBoard[0][0];
	}
	if (chessBoard[2][0] == chessBoard[2][2] && chessBoard[2][2] == chessBoard[0][2]
		&& chessBoard[0][2] != ' ')
	{
		return chessBoard[0][2];
	}

	if (is_Full(chessBoard))
	{
		return 'h';//和局
	}
	

	return ' ';//游戏继续
	
}

标签:初阶,19,MAX,chessBoard,int,printf,习题,COL,ROW
From: https://blog.csdn.net/graceyun/article/details/144819802

相关文章

  • 19删除链表的倒数第n个结点
    正常思路,先遍历一遍链表得到长度,然后进行第二次遍历得到待删除结点的上一个结点classSolution{public:ListNode*removeNthFromEnd(ListNode*head,intn){ListNode*dummyHead=newListNode(0);dummyHead->next=head;ListNode*cur=......
  • Python练习题
    序列索引和切片序列索引letters=["a","b","c","d","e","f","g","h","i","j"]print(letters[1])在Python中,列表的索引是从0开始的,即列表中第一个元素的索引为0,第二个元素的索引为1,以此类推。因此,​letter......
  • 【Vim Masterclass 笔记07】S05L19:Vim 剪切、复制、粘贴操作同步练习
    文章目录S05L19Vim剪切、复制、粘贴操作同步练习(Exercise05-Cut,CopyandPaste)1训练目标2操作指令2.1打开dyp.txt文件2.2交换文件的头两行2.3将文件首行put到文件其他为止2.4练习在光标位置的上方粘贴文本行2.5通过交换字符顺序更正存在的笔误2.6交换......
  • 第九章习题
    学号后四位:30189.2:点击查看代码importnumpyasnpfromscipy.statsimportshapirodata=np.array([15.0,15.8,15.2,15.1,15.9,14.7,14.8,15.5,15.6,15.3,15.1,15.3,15.0,15.6,15.7,14.8,14.5,14.2,14.9,14.9,1......
  • 2019浙教版 高中信息技术 必修1 数据与计算《第一章 数据与信息》大单元整体教学设计[
    2019浙教版高中信息技术必修1数据与计算《第一章数据与信息》大单元整体教学设计[2020课标]一、内容分析与整合二、《普通高中信息技术课程标准(2017年版2020年修订)》分解三、学情分析四、大主题或大概念设计五、大单元目标叙写六、大单元教学重点七、大单元教学难点八......
  • 习题9.2
    importnumpyasnpimportpandasaspdimportscipy.statsasssimportstatsmodels.apiassmimportmatplotlib.pyplotaspltplt.rcParams['font.sans-serif']=['TimesNewRoman+SimSun+WFMSansSC']plt.rcParams['mathtext.fontset......
  • 各省碳排放面板数据(1990-2022年)
    碳排放是指人类活动产生的二氧化碳(CO2)等温室气体释放到大气中的过程。通过划分排放源的范围以避免重复计算的思想,由世界资源研究所在关于企业温室气体排放清单编制的指南中首次提出。城市碳排放核算边界界定借鉴该思想,可分为3大范围一、数据介绍数据名称:各省碳排放数据......
  • dlib 19.24.6 error
         dlib19.24.6pipinstalldlibCopyPIPinstructionsLatestversionReleased: Aug10,2024 Atoolkitformakingrealworldmachinelearninganddataanalysisapplications Navigation Projectdescription Release......
  • 计算机网络复习(习题)
    术语辨析数据链路层该层在两个通信实体之间传送以帧为单位的数据,通过差错控制方法,使有差错的物理线路变成无差错数据链路。网络层负责使分组以适当的路径通过通信子网的层次。运输层负责向两台主机中进程之间的通信提供通用的数据传输服务的层次。应用层通过应用......
  • 【初阶数据结构与算法】排序算法总结篇(每个小节后面有源码)(直接插入、希尔、选择、堆
    文章目录一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序九、非递归快速排序十、非递归归并排序本篇内容将从稳定性与复杂度等角度分析排序算法,列出它们的特点、优点以及缺点,希望大家有所收获,如果想更加细......