c语言 三子棋

本文详细介绍了如何用C语言编写一个简单的三子棋游戏,包括创建test.c、game.c和game.h文件,实现棋盘初始化、打印、玩家和电脑下棋以及判断输赢的过程。通过实际代码展示,学习者能体验C语言在游戏开发中的应用。
摘要由CSDN通过智能技术生成


前言

三子棋,童年的回忆,只要三个棋子练成一条线,不论是水平、垂直、还是倾斜的直线,即为胜利。刚好最近在学C语言,就试着写个简易版三子棋,大致如下图:

一、思路

首先创建三个项目,一个test.c文件、一个game.c文件、一个game.h文件;test.c文件主要是用来实现游戏的逻辑,game.c文件用来实现每个功能模块,game.h文件用来存放头文件和宏定义。

二、代码实现

1.test.c文件

首先写一个主函数,里面有一个test函数用于测试逻辑。

int main()
{
	test();
	return 0;
}

然后是test函数的内容:

void test()
{
	srand((unsigned int)time(NULL));
	//用于电脑产生随机值时,用来初始化rand()的
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

进入test函数主要是do…while()循环,为了实现多次玩游戏;然后进入menu()函数进行简单的打印界面,如下:

void menu()
{
	printf("*********************\n");
	printf("******* 1.play ******\n");
	printf("******* 0.exit ******\n");
	printf("*********************\n");
}

do…while()循环中嵌套一个switch()函数,进行玩游戏和退出的选择,主要功能逻辑在game()函数中实现,代码如下:

#include"game.h"
void game()
{
	char Board[ROW][COL];
	//ROW和COL在game.h中定义的宏,方便后续更改
	char ret = 0;
	Init_Board(Board, ROW, COL);
	Print_Board(Board, ROW, COL);
	while (1)
	{
		Player_move(Board, ROW, COL);
		Print_Board(Board, ROW, COL);
		ret = is_win(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		Computer_move(Board, ROW, COL);
		Print_Board(Board, ROW, COL);
		ret = is_win(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
	
}

game函数中首先是对棋盘进行初始化,用Init_Board(Board, ROW, COL)函数进行初始化,传过去数组名和二维数组的行和列;然后利用Print_Board()函数打印棋盘,棋盘有了之后,下面就是开始下棋;Player_move()函数实现人下棋,下完之后利用Print_Board()函数打印;然后电脑通过Computer_move()函数实现下棋,然后打印;在while循环中进行多次下棋,要不就一次就结束了。最后定义一个字符变量ret,用来接收is_win()函数的返回值,根据不同的字符来判断谁赢,平局和继续下棋。

2.game.h文件

game.h文件主要用来存放库函数的头文件,宏定义,和test.c中函数的声明,以便game.h中方便使用;且可以通过引用game.h文件来使用其中的库函数的头文件,代码如下:

#include<stdio.h>

#include<stdlib.h>
#include<time.h>
//srand和rand所需的头文件

#define ROW 3
#define COL 3
//初始化棋盘
void Init_Board(char Board[ROW][COL],int row,int col);
//打印棋盘
void Print_Board(char Board[ROW][COL], int row, int col);
//玩家走
void Player_move(char Board[ROW][COL], int row, int col);
//电脑走
void Computer_move(char Board[ROW][COL], int row, int col);
//判断输赢函数
char is_win(char Board[ROW][COL], int row, int col);

例如此中的#include<stdio.h>头文件,test.c文件只需要#include"game.h"这样,就可以使用#include<stdio.h>和其他game.h的头文件;函数的声明,对于数组,需要写出数组元素的类型和素组的行和列

3.game.c文件

game.c文件主要进行函数的实现,也是最核心的部分

3.1棋盘初始化

代码如下,主要是对二位数组的遍历,然后将其都赋值空

void Init_Board(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Board[i][j] = ' ';
		}
	}
}

3.2棋盘打印

棋盘的打印,简易棋盘由—和|组成的,为了视觉效果,第一行打印空格字符和|,第二行打印___和|组成的,就这样组成简易棋盘
第一行:
在这里插入图片描述
1,2,3中放字符,剩下的里面放空格
第二行:
在这里插入图片描述
同样的,1,2,3位置放字符,且第二行的1,2,3和第一行的1,2,3是一样的,剩下的放空格,然后,两行组成新的一行,就如下所示:
在这里插入图片描述
将新的一行循环三次打印如下:
在这里插入图片描述
但是此时,却多了最后一行,需要限制条件,最后一行不打印,即和我们的棋盘一致了
代码如下:

void Print_Board(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", Board[i][j]);
			if (j < col-1)
				printf("|");
		}
		printf("\n");
		//上面打印第一行
		if (i <row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col-1)//最后一行限制条件
					printf("|");
			}
			printf("\n");
			//上面打印第二行
		}
	}
}

3.3玩家下棋

玩家下棋首先需要判断坐标合法化,因为棋盘是一个3x3的棋盘,横纵的坐标都只能是0-2的整数,然后再判断一下坐标是否为空格,是空格才能下,且还有一步小转化,非程序员会认为第一个位置是(1,1),但是数组里面却是(0,0);代码如下:

void Player_move(char Board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家请下棋:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		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");
		}
	}
}

3.4电脑下棋

电脑下棋是利用rand()函数产生的随机值进行对数组行和列取余得到的随机合法坐标,其中用到rand和srand函数,因为rand不能单独使用,需要srand进行初始化,srand利用时间戳进行初始化;代码如下:

void Computer_move(char Board[ROW][COL], int row, int col)
{
	printf("电脑走:>\n");
	while (1)
	{
		int x = rand()% row;
		int y = rand()% col;
		if (Board[x][y] == ' ')
		{
			Board[x][y] ='#';
			break;
		}
	}
}

3.5判断输赢

下棋的过程中,有四种情况:玩家赢、电脑赢、平局、继续下;因此定义一个字符变量ret,当其为*玩家赢,#电脑赢,Q平局,C继续下
赢:有四种:横着三子一条线,竖着三子一条线,主对角线三子一条线,副对角线三子一条线;这四种情况都是赢
平局:即棋盘被下满,但都没有赢
继续下:棋盘没满,也没有人赢
代码如下:

int is_full(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

char is_win(char Board[ROW][COL], int row, int col)
{
	for (int 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 (int j = 0; j < col; j++)
	{
		if (Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j] && Board[0][j] != ' ')
		{
			return Board[0][j];
		}
	}
	for (int i = 0; i < row; i++)
	{
		if (Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2] && Board[1][1] !=' ')
		{
			return Board[1][1];
		}
	}
	for (int i = 0; i < row; i++)
	{
		if (Board[2][0] == Board[1][1] && Board[1][1] == Board[0][2] && Board[1][1] != ' ')
		{
			return Board[1][1];
		}
	}
	if (is_full(Board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

is_full()函数是用来遍历数组,来判断都没赢的条件下,数组是否已满;此判断输赢的函数比较基础,待日后完善。

三、完整代码

//test.c全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void game()
{
	char Board[ROW][COL];
	char ret = 0;
	Init_Board(Board, ROW, COL);
	Print_Board(Board, ROW, COL);
	while (1)
	{
		Player_move(Board, ROW, COL);
		Print_Board(Board, ROW, COL);
		ret = is_win(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		Computer_move(Board, ROW, COL);
		Print_Board(Board, ROW, COL);
		ret = is_win(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
	
}
void menu()
{
	printf("*********************\n");
	printf("******* 1.play ******\n");
	printf("******* 0.exit ******\n");
	printf("*********************\n");
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

//game.h全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//初始化棋盘
 void Init_Board(char Board[ROW][COL],int row,int col);

//打印棋盘
void Print_Board(char Board[ROW][COL], int row, int col);

//玩家走
void Player_move(char Board[ROW][COL], int row, int col);

//电脑走
void Computer_move(char Board[ROW][COL], int row, int col);

//判断输赢函数
char is_win(char Board[ROW][COL], int row, int col);
//game.c全部代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void Init_Board(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Board[i][j] = ' ';
		}
	}
}

void Print_Board(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", Board[i][j]);
			if (j < col-1)
				printf("|");
		}
		printf("\n");
		if (i <row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col-1)
					printf("|");
			}
			printf("\n");
		}
	}
}

void Player_move(char Board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家请下棋:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		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 Computer_move(char Board[ROW][COL], int row, int col)
{
	printf("电脑走:>\n");
	while (1)
	{
		int x = rand()% row;
		int y = rand()% col;
		if (Board[x][y] == ' ')
		{
			Board[x][y] ='#';
			break;
		}
	}
}

int is_full(char Board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (Board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

char is_win(char Board[ROW][COL], int row, int col)
{
	for (int 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 (int j = 0; j < col; j++)
	{
		if (Board[0][j] == Board[1][j] && Board[1][j] == Board[2][j] && Board[0][j] != ' ')
		{
			return Board[0][j];
		}
	}
	for (int i = 0; i < row; i++)
	{
		if (Board[0][0] == Board[1][1] && Board[1][1] == Board[2][2] && Board[1][1] !=' ')
		{
			return Board[1][1];
		}
	}
	for (int i = 0; i < row; i++)
	{
		if (Board[2][0] == Board[1][1] && Board[1][1] == Board[0][2] && Board[1][1] != ' ')
		{
			return Board[1][1];
		}
	}
	if (is_full(Board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

总结

小游戏,大细节,受益良多,多动手,终有所获。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值