三子棋n*n棋盘的实现

本文介绍了如何实现一个N*N的三子棋游戏,包括棋盘的初始化、打印、玩家和电脑的落子逻辑以及判断输赢的算法。特别地,重点讨论了行、列和对角线上的三子连线判断,以及如何优化大棋盘的判断效率。此外,还提出了增加玩家对战功能的可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

三子棋 N*N(棋盘) 实现

思路
1.棋盘的初始化,二维数组的初始化使每个位置都为空格
2.棋盘的打印,这是一个比较难以理解的,最快的理解方法就是调试一遍
3.下棋,这分为玩家落子和电脑落子,玩家落子需考虑数组的下标是从0开始的,电脑落子都是随机数需考虑随机数的范围。
4.判断输赢,这是实现N*N棋盘的三子棋当中是最难的。你必须找到每条对角线与二维数组的规则进行判断(可以先是4 * 4试验一下)

1.分文件编写

创建一个项目
一个头文件写的是函数的声明,宏定义,库函数的包含等

一个源文件,源文件需要引用自定义的头文件自定义的引用方式为#include“game.h”

一个主体源文件文件test.c或其他名字也可以

在这里插入图片描述

1.1头文件game.h界面

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>  //时间随机数
#include<windows.h> //黑框界面操作
#define ROW 3  //行数 宏定义
#define COL 3  //列数 宏定义

//初始化棋盘
void InitBorad(char borad[][COL],int row,int col);

//打印棋盘
void DisplayBorad(char borad[][COL], int row, int col);

//玩家回合
void Player_play(char borad[][COL], int row, int col);

//电脑回合
void Computer_play(char borad[][COL], int row, int col);

//判断棋盘是否满
int IsFull(char borad[][COL], int row, int col);

//判断输赢
char IsWin(char borad[][COL], int row, int col);

1.2game.c界面

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//初始化棋盘
void InitBorad(char borad[][COL],int row,int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			borad[i][j] = ' ';//将棋盘的每个位置都设置为空格。方便落子和打印
		}
	}
}

//打印棋盘
void DisplayBorad(char borad[][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 ",borad[i][j]);//打印数据
			if(j<col-1)
			  printf("|");
		}
		printf("\n");
		if (i < row - 1)//打印分割线
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}
//玩家回合
void Player_play(char borad[][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入对应棋盘位置的坐标\n");
		printf("玩家下棋:>");
		scanf("%d%d",&x,&y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (borad[x - 1][y - 1] == ' ')
			{
				borad[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该位置已被占用,重新输入\n");
			}
		}
		else
			{
				printf("超出棋盘范围,重新输入\n");
			}
	}
}

//电脑回合
void Computer_play(char borad[][COL], int row, int col)
{
	int x = 0; 
	int y = 0; 
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		
		if (borad[x][y] == ' ')
		{
			borad[x][y] = '#';
			break;
		}
		/*else
			printf("该位置已被占用");*/
	}
}
//判断棋盘是否满
int IsFull(char borad[][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (borad[i][j] == ' ')//棋盘中还有位置是空的则表示 没有满
				return 0;
		}
	}
	return 1;//没有空的则表示满了  返回 1
}

/* * 表示 玩家赢了
   # 表示 电脑赢了
   Q 表示 平局
   C 表示 继续
*/
//判断输赢
char IsWin(char borad[][COL], int row, int col)
{
	int i = 0;
	//行
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
			{
				return borad[i][j];
			}
		}
	}
	//列
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 2; j < row; j++)
		{
			if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
			{
				return borad[j][i];
			}
		}		
	}	
	//副对角线
	for (i = 2; i < row; i++)//  这样的/ 斜向右上
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
			{
			                            //巨大问题:  borad[][]!=''要与前面的三个有关系
				return borad[i][j-2];   //返回值应该是相等中的一个
			}
		}
	}
	//主对角线
	for (i = 2; i < row; i++)//  \这样的斜向右下
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
			{
			  return borad[i][j];
		    }
		}
	}	
	if (IsFull(borad, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

1.3test.c界面

#include"game.h"

//菜单
void Show_Menu()
{
	printf("********************\n");
	printf("******1.play********\n");
	printf("******0.退出********\n");
	printf("********************\n");
}
//游戏整个过程
void Game()
{
	printf("三子棋游戏\n");
	char borad[ROW][COL] = { 0 };

	srand((unsigned)time(NULL));//随系统时间变化的随机数

	InitBorad(borad, ROW, COL);

	DisplayBorad(borad, ROW, COL);
	
	char ret;//获取IsWin()函数的返回值
	
	while (1)//循环下棋操作 直到有一方赢或平局
	{
		Player_play(borad, ROW, COL);
		DisplayBorad(borad, ROW, COL);
		ret = IsWin(borad, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		Computer_play(borad, ROW, COL);
		DisplayBorad(borad, ROW, COL);
		ret = IsWin(borad, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
		printf("玩家获胜\n");
	else if (ret == '#')
		printf("电脑获胜\n");
	else
		printf("平局\n");
	printf("3s之后返回主界面\n");
	Sleep(3000);//界面停留3秒
	system("cls");//清屏
}
int main()
{
	int chioce = 0;
	do 
	{
		Show_Menu();
		printf("请输入您的选择:>");
		scanf("%d",&chioce);
		if (chioce == 1)
		{
			system("cls");
			
			Game();
		}
	} while (chioce);


	return 0;
}

2.各个函数的实现

初始化棋盘void InitBorad(char borad[][COL],int row,int col)

void InitBorad(char borad[][COL],int row,int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			borad[i][j] = ' ';//将棋盘的每个位置都设置为空格。方便落子和打印
		}
	}
}

整个棋盘就相当与一个二维数组 只是数组遍历是从下标为零开始的在这里插入图片描述

打印棋盘void DisplayBorad(char borad[][COL], int row, int col)

该函数的实现是将整个棋盘的大概模样显示出来

void DisplayBorad(char borad[][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 ",borad[i][j]);//打印二维数组“ %c ”数据两边留有空格
			if(j<col-1)
			  printf("|");
		}
		printf("\n");
		if (i < row - 1)//打印分割线
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");//与上面的 | 和打印一起出来的 | 容易搞混 建议调试查看打印过程便于理解
			}
			printf("\n");
		}
	}
}

打印棋盘的整个过程很多地方难以理解建议F10调试看起打印过程
在这里插入图片描述

玩家回合void Player_play(char borad[][COL], int row, int col)

实现的注意事项

1. 常规的棋盘起始坐标都是从1开始,但数组的下标是从0开始的所以实现时需 为x-1,y-1 的形式才符合。列如坐标为1,1,但在二维数组中为borad[0][0];所以要将输入的x和y坐标都-1才符合数组的规则。
2.输入的x,y两个坐标必须在所定义的棋盘范围之内。
3.下的位置为占用,需重新输入。
整个过程为循环过程,玩家落子了才结束循环,到电脑的回合

void Player_play(char borad[][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入对应棋盘位置的坐标\n");
		printf("玩家下棋:>");
		scanf("%d%d",&x,&y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (borad[x - 1][y - 1] == ' ')
			{
				borad[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该位置已被占用,重新输入\n");
			}
		}
		else
			{
				printf("超出棋盘范围,重新输入\n");//超过最大棋盘范围
			}
	}
}

电脑回合void Computer_play(char borad[][COL], int row, int col)

电脑落子的实现
1.随机数的获取
直接使用rand()获取的是伪随机数,需有test.c文件中的srand((unsigned)time(NULL)); 所获得的随机数便是随系统时间变化的随机数
需包含头文件#include<time.h>
2.循环
循环获取符合没有被占用的随机数的x,y坐标
因为随机数 范围为0~row-1 和 0~col-1 所以赋给数组时不用-1

void Computer_play(char borad[][COL], int row, int col)
{
	int x = 0; 
	int y = 0; 
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;//随机数 范围为0~row-1
		y = rand() % col;//随机数 范围为0~col-1
		
		if (borad[x][y] == ' ')
		{
			borad[x][y] = '#';
			break;
		}
		/*else
			printf("该位置已被占用");*/
	}
}

判断赢方char IsWin(char borad[][COL], int row, int col)

N*N 棋盘 赢方判断

1.行、列成三子连线的实现

在这里插入图片描述
只有3 * 3以上的棋盘才能实现三子连线
遍历行时 行不变,列 变
行实现三子连线条件 举列
0行
第0列有 *或 # 第1列有 *或 # 第2列有 *或 #
数组表示:

borad[0][0] == borad[0][1] && borad[0][1] == borad[0][2] && borad[0][2] !=’ ';

第1列有 *或 # 第2列有 *或 # 第3列有 *或 #
数组表示:

borad[0][1] == borad[0][2] && borad[0][2] == borad[0][3] && borad[0][3] !=’ ';

类推
第2列有 *或 # 第2列有 *或 # 第3列有 *或 #
数组表示:

borad[0][2] == borad[0][3] && borad[0][3] == borad[0][4] && borad[0][4] !=’ ';

列与行类似就是 列 不变 行变 此处就不举例了

int i = 0;
	//行
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
			{
				return borad[i][j];
			}
		}
	}
	//列
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 2; j < row; j++)
		{
			if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
			{
				return borad[j][i];
			}
		}		
	}	

2.主对角线与副对角线

主队角线遍历规则

//主对角线
for (i = 2; i < row; i++)//  \这样的斜向右下
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
			{
			  return borad[i][j];
		    }
		}
	}	

4*4棋盘举例
在这里插入图片描述

5 * 5或是6 * 6都符合如下规则

borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ’ ’

副对角线遍历规则

	//副对角线
	for (i = 2; i < row; i++)//  这样的/ 斜向右上
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
			{
			                            //巨大问题:  borad[][]!=''要与前面的三个有关系
				return borad[i][j-2];   //返回值应该是相等中的一个
			}
		}
	}

4*4棋盘举例
在这里插入图片描述
规则

borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ’ ’

*表示 玩家赢了
#表示 电脑赢了
Q 表示 平局 棋盘被下满了就平局了 添加一个判断棋盘是否满的函数int Is Full() 返回值为int
C 表示 继续
*和#两个符号作为返回值的好处
因为 *表示玩家落子 # 表示电脑落子 直接返回所落子成为三子连线的某个位置的值即可 并且不需要繁琐的判断等

char IsWin(char borad[][COL], int row, int col)
{
	int i = 0;
	//行
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i][j - 2] == borad[i][j-1] && borad[i][j-1] == borad[i][j] && borad[i][j]!=' ')
			{
				return borad[i][j];
			}
		}
	}
	//列
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 2; j < row; j++)
		{
			if (borad[j][i] == borad[j - 1][i] && borad[j - 1][i] == borad[j - 2][i]&&borad[j][i] != ' ')
			{
				return borad[j][i];
			}
		}		
	}	
	//副对角线
	for (i = 2; i < row; i++)//  这样的/ 斜向右上
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j - 2] && borad[i][j-2] != ' ')
			{
			                            //巨大问题:  borad[][]!=''要与前面的三个有关系
				return borad[i][j-2];   //返回值应该是相等中的一个
			}
		}
	}
	//主对角线
	for (i = 2; i < row; i++)//  \这样的斜向右下
	{
		int j = 0;
		for (j = 2; j < col; j++)
		{
			if (borad[i - 2][j - 2] == borad[i - 1][j - 1] && borad[i - 1][j - 1] == borad[i][j] && borad[i][j] != ' ')
			{
			  return borad[i][j];
		    }
		}
	}	
	if (IsFull(borad, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

3.总结

1.判断输赢中的主副对角线若是棋盘太大,时间复杂度会很大,应该还有其他更好解决方法
2.可以增加实现玩家与玩家对战的功能

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值