C语言小项目——三子棋的实现

目录

1.预备知识——数组

1.1数组的定义

1.2数组的创建和初始化

1.3数组的使用 

1.4数组在内存中的储存

1.5数组越界 

2.三子棋的基本玩法

3.三子棋的C语言实现

3.1基本逻辑

3.2 打印开始菜单提供给用户

3.3使用二维数组初始化棋盘

3.4打印棋盘

3.5实现玩家下棋逻辑

3.6实现电脑下棋逻辑

3.7实现判断胜负的逻辑


1.预备知识——数组

1.1数组的定义

数组是一组相同类型元素的集合

所谓的相同元素,是指在同一个数组中,所有的元素类型必须一致。如整形数组只能存放int整形变量,字符数组只能存放char类型字符变量,不能混合存放。 


1.2数组的创建和初始化

对于数组,需要先创建和初始化才能使用,常见的数组有一维数组和二维数组,这两种数组的创建和初始化分别为:

  • 一维数组的创建
int arr[10];     //int是数组元素类型的定义,arr是数组名,[10]表明数组内元素个数是 10
  •  一维数组的初始化
int arr1[10] = { 0 };                     //将10个元素均初始化为0
int arr2[10] = { 1,2,3,4 };               //将前4个元素初始化为1,2,3,4,后6个元素初始化为0
int arr3[10] = { 1,2,3,4,5,6,7,8,9,10 };  //将10个元素初始化为1,2,3,4,5,6,7,8,9,10
int arr4[] = { 1,2,3,4 };                 //数组大小为4,并将4个元素初始化为1,2,3,4
  •  二维数组的创建
int arr[3][5];     //和一维数组类似,其中[3][5]代表3行5列,共15个元素
  • 维数组的初始化
int arr1[3][5] = { 1,2,3,4 };     //按行优先的顺序存入1,2,3,4
int arr2[3][5] = { {1,2},{4,5} }; //将1,2存入第一行,再将4,5存入第二行
int arr3[][5] = { {1,2},{4,5} };  //二维数组如果有初始化,行可以省略,列不能省略

1.3数组的使用 

对于数组的使用我们之前介绍了一个操作符:[ ] ,下标引用操作符。它其实就数组访问的操作符。 

 操作数组,首先需要知道数组下标的概念:

int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };

对于这样一个数组,它在内存中开辟了一块空间用来存放这10个元素,每个元素都有对应的下标。C语言规定,数组下标从0开始,往后依次+1

元素:1,2,3,4,5,6,7,8,9,0

下标:0,1,2,3,4,5,6,7,8,9

5497a3b755b6b6bc0f2eb5796d216f63.png

演示代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };      //数组的不完全初始化

	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的元素个数
	                           
	int i = 0; 
                
	for (i = 0; i < sz; i++) //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以i从0开始
	{
		arr[i] = i;
	}

	for (i = 0; i < 10; ++i) //输出数组的内容
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 二维数组也是同样的操作方法:

#include <stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			arr[i][j] = i * 4 + j;
		}
	}
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

总结: 

1. 数组是使用下标来访问的,下标是从0开始。
2. 数组的大小可以通过计算得到。


1.4数组在内存中的储存

对于一维数组,可以使用代码来观察数据元素在内存里的分布,在其中&为取地址符,&arr[i]表示取arr数组中下标为 i 的元素的地址。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0;i < sz;i++)
	{
		printf("&arr[%d]=%p\n", i, &arr[i]);//连续储存;%p用于打印地址(十六进制)
	}
	return 0;
}

 将结果打印出来:

仔细观察输出的结果,可以看出每当 i 加一,地址增大了4个字节,由于arr内存储的是整形变量,也占4个字节。随着数组下标的增长,元素的地址,也在有规律的递增。

同样对于二维数组:

int main()
{
	int arr[2][3] = { 0 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);         //计算行数
	int sz2 = sizeof(arr[0]) / sizeof(arr[0][0]);   //计算每行的元素个数
	for (int i = 0;i < sz1;i++)
	{
		for (int j = 0;j < sz2;j++)
		{
			printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

这样一段代码打印出来:

可以看到,对于这样一个2行3列的数组, 第0行0列的元素与第0行1列的元素地址相差4个字节,0行2列的元素和1行0列的元素地址也相差4个字节。故可知二维数组在内存中的存放模式为:

  

我们可以把二维数组看作元素类型为一维数组的一维数组,二维数组的每个元素都是一个一维数组。


由此可以得出结论:

数组在内存里是“紧挨着排列的”——既数组在内存中是连续存放的


1.5数组越界 

数组的下标是有范围限制的。
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,
所以程序员写代码时,最好自己做越界的检查。

#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
  int i = 0;
  for(i=0; i<=10; i++)
 {
    printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
 }
return 0;
}

2.三子棋的基本玩法

以下内容来自百度百科:

三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。

58c2cb5b9ce980a6622b668f5f1472fa.png

3.三子棋的C语言实现

3.1基本逻辑

  1. 打印开始菜单提供给用户
  2. 使用二维数组初始化棋盘
  3. 打印棋盘
  4. 实现玩家下棋逻辑
  5. 实现电脑下棋逻辑
  6. 实现判断胜负的逻辑

3.2 打印开始菜单提供给用户

定义一个menu函数并在test函数中进行调用,test函数又被主函数调用

void menu()
{
	printf("***************************\n");
	printf("*******  1.开始游戏  *******\n");
	printf("*******  0.退出游戏  *******\n");
	printf("***************************\n");
}

void test()
{
	int input = 0;
    do
	{
		menu();
		printf("请选择:>");
        scanf("%d", &input);
	} while (input);
}

打印效果: 

3.3使用二维数组初始化棋盘

棋盘:使用3行3列的二维数组来表示,元素类型是char,并使用宏来定义棋盘行数和列数
好处:

  1. 推高代码可读性

  2. 提高扩展性

#define ROW 3   //定义行数为3
#define COL 3   //定义列数为3
//初始化棋盘函数定义
void Initboard(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] = ' ';  
		}
	}

3.4打印棋盘

利用字符和循环打印出“井”字棋盘

//打印棋盘函数定义
void Displayboard(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 < col;j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}

实现效果:

3.5实现玩家下棋逻辑

//玩家下棋函数定义
void player_move(char board[ROW][COL], int row, int col)
{
	int a = 0;
	int b = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &a, &b);
		if (a >= 1 && a <= row && b >= 1 && b <= col)
		{
			if (board[a - 1][b - 1] == ' ')
			{
				board[a - 1][b - 1] = '*';
				break;
			}
			else
			{
				printf("此处已被占用,不能在此处下棋\n");
			}
		}
		else
		{
			printf("非法坐标!!!\n");
		}
	}
}

3.6实现电脑下棋逻辑

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋\n");
	while (1)
	{
		x = rand() % row;       //利用rand产生随机数,并控制在0~2
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

3.7实现判断胜负的逻辑

//实现一个判断棋盘是否下满的函数
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;
}

//判断胜负
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 < row;i++)
	{
		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
		{
				return board[0][i];
		}
	}

	//对角线
	if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ')
	{
		return board[0][0];
	}	
	if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}

	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续游戏
	return 'C';

最终完成效果:

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值