您现在的位置是:首页 >技术教程 >详解:三子棋以及N子棋的实现网站首页技术教程

详解:三子棋以及N子棋的实现

奶芙c 2024-06-14 17:18:23
简介详解:三子棋以及N子棋的实现


铁汁们~今天给大家分享一篇三子棋以及N子棋的实现,来吧,开造⛳️

实现流程:
1.游戏不退出,继续玩下一把(循环);
2.应用多文件的形式写代码(玩游戏实现过程),包含初始化棋盘、打印棋盘、玩家下棋、电脑下棋、判断输赢操作。

初始化棋盘

思路:初始化棋盘为空格
需要遍历棋盘(二维数组),采用双重for循环来分别控制行、列。

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] = ' ';
		}
	}
}

打印棋盘

版本1:
缺点:因为初始化棋盘为空格,直接遍历棋盘,打印棋盘,全为空格(空格在屏幕上不显示)。

void print(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]);
		}
		printf("
");
	}
}

在这里插入图片描述
版本2:
缺点:若要实现多子棋,该版本不能完成,只适用于三子棋的打印(受限制)。

void print(char board[Row][Col], int row, int col)
{
	int i = 0;
	for(i = 0; i < row; i++)
	{
			printf(" %c | %c | %c 
", board[i][0], board[i][1], board[i][2]);//打印数据
			if (i < row - 1)
				printf("---|---|---
");//打印分割线
	}
}

在这里插入图片描述
正确的版本(版本三)
思路:用双层for循环来遍历二维数组(共使用三个for循环),第一层for循环来控制’打印数据行加分割行’总次数,第二个for循环来控制数据行的打印(每次都打印‘空格数据空格|’),第三个for循环来控制打印分割行的打印(每次打印’—|')。
在这里插入图片描述

void print(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("
");  //注意
		if (i < col - 1)  //前提条件
		{
			j = 0;  //打印分割线
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
			printf("
");
		}
	}
}

在这里插入图片描述

玩家下棋

思路:输入坐标,判断下标是否合法,不合法,重新输入,合法时还需判断该坐标处是否已经被落子。

void PlayMove(char board[Row][Col], int row, int col)
{
	printf("玩家输入
");
	int x = 0;  
	int y = 0;
	while (1) //玩家要进行多次下棋(循环)
	{
		printf("请输入下棋的坐标,中间以空格隔开>:");
		scanf("%d %d", &x, &y);  
		if (x <= row && y <= col && x >= 1 && y >= 1)  //判断下标是否合法
		{                                      //合法
			if (board[x - 1][y - 1] ==' ')  //判断是否能落子
			{                               //能落子
				board[x - 1][y - 1] = '*';
				break;
			}
			else                          //不能落子
			{
				printf("坐标被占用,不能落子,请重新输入
");
			}
		}
		else           //下标不合法
		{
			printf("坐标非法,请重新输入
");
		}
	}
}

注意:0<x<=row,0<y<=col,x,y不可以等于0,因为大部分玩家并不是程序员,只有程序员才知道下标是从0开始的,为了符合大众化。

在这里插入图片描述

电脑下棋

思路:电脑产生随机坐标值,在判断坐标值是否合法,不合法无需做任何操作。

void ComputerMove(char board[Row][Col], int row, int col)
{
	printf("电脑输入
");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % 3;  //rand函数产生随机数,rand()%3产生的数值范围为0~2,需要调用srand函数
		y = rand() % 3;
		if (board[x][y] == ' ')  //判断下标是否合法
		{                        //合法
			board[x][y] = '#';
			break;
		}
	}
}

在这里插入图片描述

判断输赢

情况种类:四种情况:玩家赢,返回’*‘、 电脑赢,返回’#‘、 平局(电脑玩家均没赢,但棋盘已满),返回‘q’、 继续(电脑玩家均没赢,但棋盘未满),返回’c’.

三子棋判断输赢思路:分别进行行、列、正对角线以及负对角线的遍历,从而判断输赢。
正对角线i和j相同、负对角线i+j=row-1。

char Winner(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][2] == board[i][1] && board[i][0] != ' ')
				return board[i][0];
		}
		i = 0;
		for (i = 0; i < col; i++) //列
		{
			if (board[0][i] == board[1][i] && board[2][i] == board[1][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 (isfull(board, row, col)) //平局
		{
			return 'q'; 
		}
		else  //继续
		return 'c';
}

多子棋判断输赢思路:用双层循环遍历二维数组:处于对角线上所有元素相等时,相邻两元素相等的个数等于行数-1。
1.行的判断

第一层for循环来控制行数,第二层for循环来控制列数(循环变量<列数-1),每一行前一个元素与后一个元素进行比较,若在一行中有一对元素不相等,就跳出内层循环,出内层for循环的两种情况(已经‘有一行中各元素都相等且都不等于空格’或‘一行中各元素不相等’),而对于‘有一行中各元素都相等且都不等于空格’此情况,此时循环变量等于列数-1,直接返回该行中的最后一个元素。

2.列的判断

第一层for循环来控制列数,第二层for循环来控制行数(循环变量<行数-1) ,每一列前一个元素与后一个元素进行比较,若在一列中有一对元素不相等,就跳出内层循环,出内层for循环的两种情况(已经‘有一列中各元素都相等且都不等于空格’或‘一列中各元素不相等’),而对于‘有一列中各元素都相等且都不等于空格’此情况,此时循环变量等于行数-1,直接返回该列中的最后一个元素。

3.正对角线的判断

由观察可知,在正方体中处于正对角线的元素行数和列数相等,此时只需要行数和列数分别都加一就可以找到下一个元素,从而来遍历正对角线,前一个元素与后一个元素进行比较,创建一个计数器来记录相邻两元素相等的个数,每次两元素相等个数就加一,由于当正对角线上所有元素相等且不等于空格时,相邻两元素相等的个数等于行数-1,当出了循环只需判断计数器的个数是否等于行数-1且不等于空格,相等就直接返回正对角线上任意元素。

4.负对角线的判断:

由观察可知,在正方体中处于负对角线的元素行数加列数相等行数-1,此时只需要行数+1、列数-1就可以找到下一个元素,从而来遍历负对角线,前一个元素与后一个元素进行比较,创建一个计数器来记录相邻两元素相等的个数,每次两元素相等个数就加一,由于当负对角线上所有元素相等且不等于空格时,相邻两元素相等的个数等于行数-1,当出了循环只需判断计数器的个数是否等于行数-1且不等于空格,相等就直接返回正对角线上任意元素。

char Winner(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 - 1; j++)
		{
			if (board[i][j] != board[i][j + 1])//若一行中有一个不相等就跳出一行的循环
				break;
		}
		if (j == col - 1 && board[i][j] != ' ')//相等但不能为空格
			return board[i][j];
	}
	i = 0;//列
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 0; j < row - 1; j++)
		{
			if (board[j][i] != board[j + 1][i])//若一列中有一个不相等就跳出一行的循环
				break;
		}
		if (j == row - 1 && board[j][i] != ' ')
			return board[j][i];
	}
	int count = 0;//用来记录对角线上相邻元素相等的个数
	i = 0;
	for (i = 0; i < row-1; i++)//对角线()
	{
		if (board[i][i] == board[i + 1][i + 1]&&board[i][i]!=' ')
		{
			count++;
		}
		if (count == row - 1)
			return board[i][i];
	}
	i = 0;//对角线(/)
	for (i = 0; i < row-1; i++)
	{
		int j = 0;
		for (j =col-1; j>=1;j--)
		{
			if (i + j ==row - 1)
			{
				if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
				{
					count++;
				}
				if (count == row - 1)
					return board[i][j];
			}
		}
	}

主函数的实现(test.c)

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menue()  //菜单栏
{
	printf("****************
");
	printf("**** 1.play ****
");
	printf("**** 0.exit ****
");
	printf("****************
");
}

void game()
{
	char board[Row][Col] = { 0 };//因在玩游戏过程中要进行数据的存储,采用Row*Col的二维数组进行存储有效数据
	InitBoard(board, Row, Col);
	char ret = 0;
	while (1)
	{
		PlayMove(board, Row, Col); //玩家下棋
		print(board, Row, Col);  //打印棋盘
		ret = Winner(board, Row, Col); //判断输赢
		if (ret != 'c')
			break;
		ComputerMove(board, Row, Col);  //电脑下棋
		print(board, Row, Col);  //打印棋盘
		ret = Winner(board, Row, Col); //判断输赢
		if (ret != 'c')
			break;
	}
	if (ret == '*')
		printf("玩家赢
");
	else if (ret == '#')
		printf("电脑赢
");
	else
		printf("平局
");
}

int main()
{
	srand((unsigned int)time(NULL)); //产生随机数时函数的调用(rand->srand->time),srand函数调用#include<stdlib.h>头文件,time函数调用#include<time.h>头文件
	int input = 0;
	do     //先进行菜单栏的选择,若玩完一把还想在玩->循环(do..while循环体执行次数比条件判断次数多一)
	{
		menue();
		printf("请选择<:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏
");
			break;
		default:
			printf("输入错误,请重新选择
");
			break;
		}
	} while (input);
	return 0;
}

game.c的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
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] = ' ';
		}
	}
}

void print(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("
");
		if (i < col - 1)  //打印分割线
		{
			j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
			printf("
");
		}
	}
}

void PlayMove(char board[Row][Col], int row, int col)  //玩家下棋
{
	printf("玩家输入
");
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入下棋的坐标,中间以空格隔开>:");
		scanf("%d %d", &x, &y);
		if (x <= row && y <= col && x >= 1 && y >= 1) //判断下标是否合法
		{
			if (board[x - 1][y - 1] == ' ')  //合法,判断是否能落子
			{
				board[x - 1][y - 1] = '*';//能落子
				break;
			}
			else    //不能落子
			{
				printf("坐标被占用,不能落子,请重新输入
");
			}
		}
		else
		{
			printf("坐标非法,请重新输入
");
		}
	}
}

void ComputerMove(char board[Row][Col], int row, int col)  //电脑下棋
{
	printf("电脑输入
");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % 3;  //产生随机坐标(合法)
		y = rand() % 3;
		if (board[x][y] == ' ')  //坐标为被占用
		{
			board[x][y] = '#';
			break;
		}
	}
}

int isfull(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++)
		{
			if (board[i][j] == ' ')  //没满
				return 0;
		}
	}
	return 1;  //满了
}

char Winner(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 - 1; j++)
		{
			if (board[i][j] != board[i][j + 1])//若一行中有一个不相等就跳出一行的循环
				break;
		}
		if (j == col - 1 && board[i][j] != ' ')//相等但不能为空格
			return board[i][j];
	}
	i = 0;//列
	for (i = 0; i < col; i++)
	{
		int j = 0;
		for (j = 0; j < row - 1; j++)
		{
			if (board[j][i] != board[j + 1][i])//若一列中有一个不相等就跳出一行的循环
				break;
		}
		if (j == row - 1 && board[j][i] != ' ')
			return board[j][i];
	}
	int count = 0;//用来记录对角线上相邻元素相等的个数
	i = 0;
	for (i = 0; i < row-1; i++)//对角线()
	{
		if (board[i][i] == board[i + 1][i + 1]&&board[i][i]!=' ')
		{
			count++;
		}
		if (count == row - 1)
			return board[i][i];
	}
	i = 0;//对角线(/)
	for (i = 0; i < row-1; i++)
	{
		int j = 0;
		for (j =col-1; j>=1;j--)
		{
			if (i + j ==row - 1)
			{
				if (board[i][j] == board[i + 1][j - 1] && board[i][j] != ' ')
				{
					count++;
				}
				if (count == row - 1)
					return board[i][j];
			}
		}
	}

	/*for (i = 0; i < row; i++)  //三子棋的判断
	{
		if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][0] != ' ')
			return board[i][0];
	}
	i = 0;
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[2][i] == board[1][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 (isfull(board, row, col))  //平局的判断
	{
		return 'q';
	}
	return 'c';
}

game.h的实现

作用:用于存放声明自定义函数

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define Row 3  //便于实现多子棋时,行、列的更改
#define Col 3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void InitBoard(char board[Row][Col], int row, int col);
void print(char board[Row][Col], int row, int col);
void PlayMove(char board[Row][Col], int row, int col);
void ComputerMove(char board[Row][Col], int row, int col);
char Winner(char board[Row][Col], int row, int col);
int isfull(char board[Row][Col], int row, int col);
运行结果

在这里插入图片描述

铁铁们,分支语句和循环语句就到此结束啦,请动动你们的手给作者点个?鼓励吧,你们的鼓励就是我的动力✨✨✨
在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。