三子棋(N子棋)——C语言实现

请添加图片描述

一.三子棋介绍

  三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。本篇文章将根据所学数组内容进行应用,设计出一个简易的三子棋游戏

二.设计思路

(一)设计出一个菜单引导玩家进行游戏

(二)设计出游戏大体框架
1.设计菜单实现程序(选择开始游戏/退出游戏,输错提示)
2.创建一个3×3的数组作为棋盘;
3.初始化棋盘并打印棋盘;
4.设计下棋步骤,包括玩家下棋和电脑下棋;
5.设计判断输赢、和棋函数;

三.设计程序

我们上手来写时,发现这是一段非常长的代码,在一个文件里写出来可读性不高。这里建议大家分文件去写,还能保证程序的封装性,可读性和易维护性。我们分三个文件来写:

test.c 测试游戏的程序
game.c 游戏函数的具体实现
game.h 游戏函数的所有声明,头文件,宏定义等。

1.设计菜单

设计一个函数打印菜单

//写在test.c里
void menu()
{
	printf("**************************************\n");
	printf("**********    三子棋游戏  ************\n");
	printf("********** 选择1:开始游戏 ***********\n");
	printf("********** 选择0:退出游戏 ***********\n");
	printf("**************************************\n");
}

菜单功能包括输入“1”:“开始游戏”,输入“0”:“退出游戏”,输入其他就要提示输错。如果玩完一把后不用退出程序重新开始,我们将这个选择程序放在一个循环中。菜单要重复打印,所以把menu()放在循环中,因为最开始要先打印一次菜单,所以我们用do…while…循环

int main()
{
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	
	return 0;
}

测试:
请添加图片描述

2.设计棋盘

具体设计棋盘时,我们发现我们先要创建一个棋盘,并初始化,然后还要实现打印。

1.初始化棋盘

我们开始写初始化棋盘函数
写在game.c中的:

void InitBoard(char board[3][3], 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] = ' ';
		}
	}
}

写在game.h中的:

void InitBoard(char board[3][3], int row, int col);

我们这样写发现后续若要改为五子、十子等棋,N子棋时,程序中的3全都要改一遍极其麻烦,于是我们可以宏定义行和列,这样只需要改这两个定义即可转换成多子棋。
game.h中写:

#define ROW 3
#define COL 3

修改上述函数:
写在game.c中的:

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

写在game.h中的:

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

2.打印棋盘

我们这里提供的方法是将数据和分割线分开打印
请添加图片描述
Tip:因为我们不打印最后一个“|”,所以打印“|”前需要进行判断。

game.h中声明:

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

game.c中实现:

void Display(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				//同理
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}	
}

测试代码:

//写在test.c中的
void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board, ROW, COL);
	//打印棋盘
	Display(board, ROW, COL);
}

测试
请添加图片描述

3.设计下棋步骤

1.玩家下棋

玩家此时输入一个坐标表示下棋的位置,行要大于等于1小于等于3
列要大于等于1小于等于3
这时下的坐标会有几种情况:
1.此坐标被占用,已有棋子,提示"坐标已有子,请重新输入"
2.此坐标非法,即超出了棋盘(3×3)范围,提示"坐标非法,请重新输入"
3.此坐标为空格,可以下棋

game.c中实现:

//玩家下棋
void Playermove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋:\n");
	
	while (1)
	{
		printf("请输入下棋坐标,中间使用空格:");
		scanf("%d %d", &x, &y);
		//printf("玩家下棋:\n");
		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");
		}

	}
}

我们这时再来测试一下:
测试代码:

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board, ROW, COL);
	//打印棋盘
	Display(board, ROW, COL);
	while (1)
	{
		Playermove(board, ROW, COL);
		Display(board, ROW, COL);//打印棋盘来看我们输入结果
	}
}

请添加图片描述

2.电脑下棋

我们这里采用的方法是电脑随机下棋,这种方法较为简单地实现电脑下棋。当然我们也可以设计进攻算法和防守算法来强化电脑,此处我们仅是实现简易的三子棋,故不多赘述。
我们对电脑x,y坐标生成随机数,依然是使用我们的rand,srand函数,详细使用方法在猜数字游戏中已经讲过。

game.c 中实现:

//电脑下棋
void Computermove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:\n");
	while (1)
	{
		x = rand() % row;//生成0~2的数
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
				break;
		}
	}
}

test.c 中测试:

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board, ROW, COL);
	//打印棋盘
	Display(board, ROW, COL);
	while (1)
	{
		Playermove(board, ROW, COL);
		Display(board, ROW, COL);

		Computermove(board, ROW, COL);
		Display(board, ROW, COL);
	}
}

int main()
{
	srand((unsigned int)time(NULL));//
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

测试:
请添加图片描述

4.设计判断输赢函数

赢的条件是行或列或对角线三子连在一起。
请添加图片描述
1.当玩家赢了返回"*“;
2.当电脑赢了返回”#“;
3.当棋盘满了且胜负未分返回"Q
4.若胜负未分且棋盘未满则继续,返回‘C’。

判断棋盘是否满了还要设计一个函数来判断:
game.c中实现:

//判断棋盘是否满了
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;
}

判断输赢函数实现
game.c中实现:

//判断输赢

//玩家:*
//电脑赢:#
//平局:Q
//继续:C
char Iswin(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 - 1; j++)
		{
			if (board[i][j] != board[i][j + 1]|| board[i][j] == ' ')
				break;
			if(j==col-2)
			return board[i][j];
		}
	}
	//列满足
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row - 1; i++)
		{
			if (board[i][j] != board[i + 1][j]|| board[i][j] == ' ')
				break;
			if(i==row-2)
			return board[i][j];
		}
	}
	//对角线满足
	for (i = 0, j = 0; i < row - 1, j < col - 1; i++, j++)
	{
		if (board[i][j] != board[i + 1][j + 1]|| board[i][j] == ' ')
			break;
		if(i==row-2)
		return board[i][j];
	}
	for (i = 0, j = col - 1; i < row - 1, j>0; i++, j--)
	{
		if (board[i][j] != board[i + 1][j - 1]|| board[i][j] == ' ')
			break;
		if(i==row-2)
		return board[i][j];
	}
	//平局
	if (Isfull(board, row, col)==1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

Tip:我们用循环的方式一个个访问数组里的元素,判断是第几种情况。
我们可以使用*/#来判断输赢,这时无论是玩家赢还是电脑赢返回时直接返回board[i][j]就行。而不需要单独判断是玩家赢了还是电脑赢了。

四.调试实例

请添加图片描述
请添加图片描述
请添加图片描述

五.完整代码参考

game.h:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

void InitBoard(char board[ROW][COL], int row, int col);
void Display(char board[ROW][COL], int row, int col);
void Playermove(char board[ROW][COL], int row, int col);
void Computermove(char board[ROW][COL], int row, int col);
int Isfull(char board[ROW][COL], int row, int col);
char Iswin(char board[ROW][COL], int row, int col);

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 Display(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}	
}
//玩家下棋
void Playermove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋:\n");
	
	while (1)
	{
		printf("请输入下棋坐标,中间使用空格:");
		scanf("%d %d", &x, &y);
		//printf("玩家下棋:\n");
		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");
		}

	}
}
//电脑下棋
void Computermove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		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;
}
//判断输赢

//玩家:*
//电脑赢:#
//平局:P
//继续:C
char Iswin(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 - 1; j++)
		{
			if (board[i][j] != board[i][j + 1]|| board[i][j] == ' ')
				break;
			if(j==col-2)
			return board[i][j];
		}
	}
	//列满足
	for (j = 0; j < col; j++)
	{
		for (i = 0; i < row - 1; i++)
		{
			if (board[i][j] != board[i + 1][j]|| board[i][j] == ' ')
				break;
			if(i==row-2)
			return board[i][j];
		}
	}
	//对角线满足
	for (i = 0, j = 0; i < row - 1, j < col - 1; i++, j++)
	{
		if (board[i][j] != board[i + 1][j + 1]|| board[i][j] == ' ')
			break;
		if(i==row-2)
		return board[i][j];
	}
	for (i = 0, j = col - 1; i < row - 1, j>0; i++, j--)
	{
		if (board[i][j] != board[i + 1][j - 1]|| board[i][j] == ' ')
			break;
		if(i==row-2)
		return board[i][j];
	}
	//平局
	if (Isfull(board, row, col)==1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
	printf("**************************************\n");
	printf("**********    三子棋游戏  ************\n");
	printf("********** 选择1:开始游戏 ***********\n");
	printf("********** 选择0:退出游戏 ***********\n");
	printf("**************************************\n");
}

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	InitBoard(board, ROW, COL);
	//打印棋盘
	Display(board, ROW, COL);
	//下棋步骤
	char ret = 0;
	while (1)
	{
		//玩家下棋
		Playermove(board, ROW, COL);
		Display(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
			break;
		//电脑下棋
		Computermove(board, ROW, COL);
		Display(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else
		printf("平局\n");
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

N子棋

请添加图片描述
请添加图片描述

总结

此次的三子棋设计实验,能提高我们对分文件处理程序设计的能力,在本篇文章的程序中通过修改宏定义的ROW,COL即可实现多种棋程序的转换,我们成功的实现了多子棋,提高了程序的易维护性,代码还有许多不足还需日后完善,还望各位大佬多多提出建议。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值