[C语言初阶]三子棋小游戏

目录

一、原理及问题分析

二、代码实现

2.1 test.c文件中

2.1.1 游戏框架搭建

2.2.2 游戏菜单函数实现

2.2.3 game()函数部分

2.2.4 test.c文件内全部代码呈现

2.2 game.c文件中

2.2.1  棋盘初始化与打印

2.2.2 玩家与电脑下棋逻辑

2.2.3 输赢判断与游戏状态

2.2.4 gmae.c文件内全部代码呈现

2.3 game.h文件中

总结


在上一篇文章中,我们介绍了C语言中的数组。今天,我们将结合我们之前所学的所有内容,制作出我们的第二个项目——三子棋小游戏。


一、原理及问题分析

三子棋是一款经典的双人对战游戏,玩家通过轮流在3x3的棋盘上落子,先连成一条线(横、竖、对角线)的一方获胜。在这个游戏中,玩家与电脑对战,电脑采用随机落子策略。游戏的整体流程如下:

  1. 初始化棋盘:创建3x3的二维数组,初始化为空格。
  2. 打印棋盘:动态显示当前棋盘状态,方便玩家观察。
  3. 玩家下棋:输入坐标并校验合法性(范围、是否被占用)。
  4. 电脑下棋:随机生成合法坐标。
  5. 判断游戏状态:每次落子后检查是否达成胜利条件或平局。
  6. 循环游戏:单局结束后可选择继续游玩或退出。

关键问题

  • 模块化设计:将函数声明、定义、测试逻辑拆分到不同文件(test.c、game.h、game.c),提高代码可维护性。
  • 通用性优化使用符号常量(ROWCOL)定义棋盘大小,便于后期扩展修改,不用一个一个改麻烦。
  • 输赢判断:需遍历行、列、对角线,检查是否连成三子。

二、代码实现

2.1 test.c文件中

2.1.1 游戏框架搭建

游戏主流程使用do-while循环,结合switch语句处理玩家选择(这一点我们在之前的拆数字小游戏中已经实现过了):

int main()
{
	int input=0;
	srand((unsigned)time(NULL));
	menu();
	int count = 0;
	do
	{
		if (count == 0)
		{
			printf("请输入(选择1开始游戏,选择0退出游戏):>");
		}
		else
		{
			printf("是否进行下一局(选1继续游玩,选0退出游戏):>");
		}
		scanf("%d", &input);
		switch (input)//游戏整体流程
		{
		case 1:
			printf("开始游戏\n");
			game();
			count++;
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while(input);

}

2.2.2 游戏菜单函数实现

游戏菜单我们在猜数字小游戏中已经介绍过,就是有限次的printf打印:

void menu()//菜单函数
{
	printf("*****************************\n");
	printf("*****************************\n");
	printf("*********三子棋游戏**********\n");
	printf("*****************************\n");
	printf("*********1.开始游戏**********\n");
	printf("*********0.退出游戏**********\n");
	printf("********版本:Beta1.0*********\n");
	printf("********作者:Yang210*********\n");
	printf("*****************************\n");
	printf("*****************************\n");


}

2.2.3 game()函数部分

game()函数是游戏的主体逻辑部分,里面放着用来实现三子棋小游戏各种功能的函数,但是这些函数不在game()函数内声明和实现,我们只是在game()函数中调用这些函数,而具体的每一个函数实现在game.c文件中,而声明在game.h文件中,如此提高代码的可读性。

void game()//游戏主体逻辑
{
	char arr[ROW][COL];
	int choose = 0;
	char flag = 0;
	start(arr, ROW, COL);//棋盘初始化函数
	print_board(arr, ROW, COL);//棋盘打印函数
	while (1)
	{
		player(arr, ROW, COL);//玩家下棋函数
		print_board(arr, ROW, COL);
		flag=is_win(arr, ROW, COL);//判断输赢函数
		if (flag != 'C')
		{
			break;
		}
		computer(arr, ROW, COL);//电脑下棋函数
		Sleep(3000);
		print_board(arr, ROW, COL);
		flag = is_win(arr, ROW, COL);//判断输赢函数
		if (flag != 'C')
		{
			break;
		}
	}
	if (flag == '*')
	{
		printf("玩家获胜!\n");
	}
	else if (flag == '#')
	{
		printf("电脑获胜!\n");
	}
	else
	{
		printf("此局平局!\n");
	}
	print_board(arr, ROW, COL);
	
}

2.2.4 test.c文件内全部代码呈现

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()//菜单函数
{
	printf("*****************************\n");
	printf("*****************************\n");
	printf("*********三子棋游戏**********\n");
	printf("*****************************\n");
	printf("*********1.开始游戏**********\n");
	printf("*********0.退出游戏**********\n");
	printf("********版本:Beta1.0*********\n");
	printf("********作者:Yang210*********\n");
	printf("*****************************\n");
	printf("*****************************\n");


}

void game()//游戏主体逻辑
{
	char arr[ROW][COL];
	int choose = 0;
	char flag = 0;
	start(arr, ROW, COL);//棋盘初始化函数
	print_board(arr, ROW, COL);//棋盘打印函数
	while (1)
	{
		player(arr, ROW, COL);//玩家下棋函数
		print_board(arr, ROW, COL);
		flag=is_win(arr, ROW, COL);//判断输赢函数
		if (flag != 'C')
		{
			break;
		}
		computer(arr, ROW, COL);//电脑下棋函数
		Sleep(3000);
		print_board(arr, ROW, COL);
		flag = is_win(arr, ROW, COL);//判断输赢函数
		if (flag != 'C')
		{
			break;
		}
	}
	if (flag == '*')
	{
		printf("玩家获胜!\n");
	}
	else if (flag == '#')
	{
		printf("电脑获胜!\n");
	}
	else
	{
		printf("此局平局!\n");
	}
	print_board(arr, ROW, COL);
	
}

int main()
{
	int input=0;
	srand((unsigned)time(NULL));\\初始化随机数,在猜数字小游戏介绍过
	menu();
	int count = 0;
	do
	{
		if (count == 0)
		{
			printf("请输入(选择1开始游戏,选择0退出游戏):>");
		}
		else
		{
			printf("是否进行下一局(选1继续游玩,选0退出游戏):>");
		}
		scanf("%d", &input);
		switch (input)//游戏整体流程
		{
		case 1:
			printf("开始游戏\n");
			game();
			count++;
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while(input);

}

2.2 game.c文件中

2.2.1  棋盘初始化与打印

棋盘初始化

// game.c
void start(char arr[ROW][COL], int row, int col)//1.棋盘初始化函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			arr[i][j] = ' ';
		}
	}
}

动态打印棋盘

void print_board(char arr[ROW][COL], int row, int col)//2.棋盘打印函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)//打印数据行
	{
		for (j = 0;j < col;j++)
		{
			printf(" %c ", arr[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		putchar('\n');
		if (i < row - 1) //打印分割行
		{
			for (j = 0;j < col;j++)
			{

				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		
		putchar('\n');
	}
}

2.2.2 玩家与电脑下棋逻辑

玩家下棋

void player(char arr[ROW][COL], int row, int col)//3.玩家下棋函数实现
{
	int x = 0;
	int y = 0;
	printf("当前回合玩家下棋(玩家代表*)\n");
	while (1)
	{
		printf("请输入坐标(如:2 3表示第二行第三列):>");
		scanf("%d %d", &x, &y);
		if (x > row || y > col)
		{
			printf("坐标非法,请重新输入\n");
		}
		else if (x >= 1 && y >= 1 && x <= row && y <= col)
		{
			if (arr[x-1][y-1] == ' ')
			{
				arr[x-1][y-1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用,请重新输入\n");
			}
		}
	}
}

电脑下棋

void computer(char arr[ROW][COL], int row, int col)//4.电脑下棋函数实现
{
	printf("当前回合电脑下棋\n");
	while (1)
	{
		int x = rand() % 3;//若row为3,则余数为0-2,这是一个重要的技巧
		int y = rand() % 3;
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '#';
			break;
		}
	}
}

2.2.3 输赢判断与游戏状态

判断胜负(关于这部分只限于三子棋情况,还不够通用,后期可以参照打印棋盘的思路优化)

char is_win(char arr[ROW][COL], int row, int col)//5.判断输赢函数实现
{
	int i = 0;
	for (i = 0;i < row;i++)//判断行
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][1] != ' ')
		{
			return arr[i][1];
		}
	}
	for (i = 0;i < col;i++)//判断列
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[1][i] != ' ')
		{
			return arr[1][i];
		}
	}
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1] != ' ')//判断主对角线
	{
		return arr[1][1];
	}
	else if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')//判断副对角线
	{
		return arr[1][1];
	}
	 int ret= is_Full(arr, row, col);//判断是否平局的函数
	 if (ret == 1)
	 {
		 return 'C';
	 }
	 else
	 {
		 return 'S';
	 }

}

判断棋盘是否满了(是否平局): 

int is_Full(char arr[ROW][COL], int row, int col)//6.判断棋盘是否满了函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			if (arr[i][j] == ' ')
			{
				return 1;
			}
		}
	}
	return 0;
}

2.2.4 gmae.c文件内全部代码呈现

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void start(char arr[ROW][COL], int row, int col)//1.棋盘初始化函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			arr[i][j] = ' ';
		}
	}
}

void print_board(char arr[ROW][COL], int row, int col)//2.棋盘打印函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			printf(" %c ", arr[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		putchar('\n');
		if (i < row - 1) 
		{
			for (j = 0;j < col;j++)
			{

				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		
		putchar('\n');
	}
}

void player(char arr[ROW][COL], int row, int col)//3.玩家下棋函数实现
{
	int x = 0;
	int y = 0;
	printf("当前回合玩家下棋(玩家代表*)\n");
	while (1)
	{
		printf("请输入坐标(如:2 3表示第二行第三列):>");
		scanf("%d %d", &x, &y);
		if (x > row || y > col)
		{
			printf("坐标非法,请重新输入\n");
		}
		else if (x >= 1 && y >= 1 && x <= row && y <= col)
		{
			if (arr[x-1][y-1] == ' ')
			{
				arr[x-1][y-1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用,请重新输入\n");
			}
		}
	}
}

void computer(char arr[ROW][COL], int row, int col)//4.电脑下棋函数实现
{
	printf("当前回合电脑下棋\n");
	while (1)
	{
		int x = rand() % 3;
		int y = rand() % 3;
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '#';
			break;
		}
	}
}

char is_win(char arr[ROW][COL], int row, int col)//5.判断输赢函数实现
{
	int i = 0;
	for (i = 0;i < row;i++)
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][1] != ' ')
		{
			return arr[i][1];
		}
	}
	for (i = 0;i < col;i++)
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[1][i] != ' ')
		{
			return arr[1][i];
		}
	}
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1] != ' ')
	{
		return arr[1][1];
	}
	else if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
	{
		return arr[1][1];
	}
	 int ret= is_Full(arr, row, col);
	 if (ret == 1)
	 {
		 return 'C';
	 }
	 else
	 {
		 return 'S';
	 }

}

int is_Full(char arr[ROW][COL], int row, int col)//6.判断棋盘是否满了函数实现
{
	int i = 0;
	int j = 0;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			if (arr[i][j] == ' ')
			{
				return 1;
			}
		}
	}
	return 0;
}


2.3 game.h文件中

头文件用于声明函数和常量:

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

void start(char arr[ROW][COL], int row, int col);//1.棋盘初始化函数声明

void print_board(char arr[ROW][COL], int row, int col);//2.棋盘打印函数声明

void player(char arr[ROW][COL], int row, int col);//3.玩家下棋函数声明

void computer(char arr[ROW][COL], int row, int col);//4.电脑下棋函数声明

char is_win(char arr[ROW][COL], int row, int col);//5.判断输赢函数声明

int is_Full(char arr[ROW][COL], int row, int col);//6.判断棋盘是否满了函数声明

 注意:虽然这部分文件我们放到了这篇文章中的最后一部分讲,但是game.h文件要先引用到test.c和game.c两个文件的开始部分中去。因为是自己写的头文件,所以引用方式为双引号 #include "game.h"。game.h文件并不是最后才写的,而是我们在实现game.c中的函数的功能时顺便,同时进行的。


总结

后期优化方向

  • 提升电脑AI:当前AI是随机下棋,比较笨,后期有能力了我们可以优化算法,使其能主动拦截玩家或寻找获胜机会。
  • 扩展为N子棋(如五子棋),需优化输赢判断逻辑的通用性。

完整的代码已上传至​​​​​​​三子棋小游戏 · 暮鹤筠/C language - 码云 

最后,送给大家一句话“编程如对弈,步步为营,方能制胜。”下一篇文章,我们将继续运用我们现在所学的知识实现我们的第三个项目——扫雷小游戏,敬请期待!


作者其他文章链接:

[C语言初阶]猜数字小游戏实现_c语言猜数字游戏流程图-CSDN博客
Gitee详细使用教程_gitee使用教程-CSDN博客
[C语言初阶]函数-CSDN博客
[C语言初阶]递归-CSDN博客
[C语言初阶]数组-CSDN博客 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值