C语言实现三子棋

目录

1.游戏准备

2.主函数

3.test函数

4.menu函数

5.game函数

6.棋盘初始化

7.棋盘打印

8.玩家下棋

9.电脑下棋

10.判断输赢

11.游戏完整代码展示

11.1 game.h 游戏头文件

11.2 game.c 游戏逻辑实现

11.3 程序运行逻辑 


前言:博主目前学习到了二维数组,结合网上的教程,实现了一个三子棋小游戏,虽然和我下棋的电脑憨憨的,让它赢都可能赢不了的那种。但是写程序的过程以及实现游戏后让人非常有成就感,同时也能很好的总结之前学的知识点(这个游戏运用了很多之前的知识,需要你对它们有透彻的理解)。


实现效果如图(故意让电脑赢的),由于博主水平有限,实现的样子简陋,希望不久的将来能写出图形化界面(等我回来修改它)

话不多说,我们开始吧。

1.游戏准备

要实现一个游戏我们会写很多的代码,为了不让代码看得混乱,同时也确保代码的整洁性,我把整个代码分成了三个部分。

game.c  ---> 存放游戏相关函数

game.h ---> 游戏相关函数以及库函数的声明

test.c ---> 游戏的逻辑实现。

1.玩家下的棋子我们用 ' * ' 表示,电脑下的棋子我们用‘ # ’表示

2.下的棋子当然是要放进棋盘中,我们用二维数组代表棋盘

3.棋盘的大小我们用宏来定义,方便我们修改

4.在实现函数时,确保函数只实现一个功能,使其成为独立的代码块

2.主函数

int main()
{
	srand((unsigned int)time(NULL));//设置随机数种子
	test();

	return 0;
}

为什么要使用srand函数后面就会知道了哈,等等我会解释

在函数中调用test函数,test实现全部代码的逻辑

3.test函数

void test()
{
	int input = 0;
	do
	{
		menu();//打印菜单,让玩家选择是否开始游戏
		printf("请选择 > 1/0 \n");
		scanf("%d", &input);//input为是否游戏的判断依据
		switch (input)//switch语句进入不同分支
		{
		case 1:
			game();//game函数实现游戏过程的逻辑
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);//循环,让玩家决定是否继续游戏
}

4.menu函数

void menu()
{
	printf("***********************\n");
	printf("****  1.开始游戏   ****\n");
	printf("****  0.退出游戏   ****\n");
	printf("***********************\n");
}

接着是game函数的实现,game是游戏过程的逻辑:初始化棋盘 --> 打印棋盘 --> 玩家下棋 --> 电脑下棋 --> 判断谁赢得比赛,比赛有四种情况,我们用四种字符来表示

'#' --- 电脑胜利

'*' --- 玩家胜利

'C' --- 游戏继续

'p' --- 游戏平局

为什么要用字符表示游戏的状态?因为我们可以根据字符输入游戏结果,或者让游戏继续。这又是什么意思?别急,我们再来看看game函数。

5.game函数

game函数实现游戏的逻辑,根据我们梳理的逻辑先来试着理解一下游戏的实现过程

void game()
{
	char board[ROW][COL];//棋盘的创建
	InitBoard(board, ROW, COL);//初始化棋盘
	DisplayBoard(board, ROW, COL);//给玩家展示棋盘
	char con = 0;//判断游戏是否继续的变量
	
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋
		DisplayBoard(board, ROW, COL);//下完棋后展示棋盘

		//每一次下完棋后都要判断双方谁赢
		con = is_win(board, ROW, COL);//该函数会返回我所列出的四种情况所对应的四种字符
		if (con != 'C')//根据is_win函数的返回值决定游戏是否继续
		{
			break;
		}


		ComMove(board, ROW, COL);//电脑下棋
		DisplayBoard(board, ROW, COL);//展示棋盘

		//每次下完棋的判断
		con = is_win(board, ROW, COL);
		if (con != 'C')
		{
			break;
		}
	}//注意我设置了一个死循环
	//如果is_win函数返回继续游戏的字符,重复下棋的函数,
	//下棋的动作会一直循环直到返回其他字符,跳出循环

	//根据返回值打印游戏结果
	if (con == '*')
	{
		printf("玩家获胜\n");
	}
	else if (con == '#')
	{
		printf("电脑获胜\n");
	}
	else if(con == 'Q')
	{
		printf("平局\n");
	}
}

现在理解了吗?con为字符变量,它用来接收is_win函数返回的字符,程序再根据con的字符判断是否要继续游戏,还是要结束游戏,打印游戏结果。 

6.棋盘初始化

由于每个函数的参数基本相同,代表的意义也类似,这里先说明每个参数的意思,之后不再赘述

char board[ROW][COL]:用二维数组接收我们创建的棋盘,行与列为宏定义的常量,方便我们对棋盘的修改

int row, int col :用row与col存放数组的行与列,在循环时我们会用到这两个变量


void InitBoard(char board[ROW][COL], int row, int col)
{
	//用两层for循环遍历数组,把数组的每个元素初始化为空格
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

7.棋盘打印

这样一个棋盘要如何打印?从一行来看,一个格子由空格、字符、空格组成,然后是一个 | 用来分割棋盘,当打印第三次空格、字符、空格时最后的 | 不打印。接着是三个 - 和一个 | ,当第三次打印---时 | 不打印(对于不打印我们用if语句来实现)。

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	//两层for循环打印
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			//一行中打印空格、字符、空格
			printf(" %c ",board[i][j]);
			//if语句限制'|'出现次数
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");//打印完一行记得要换行
		//三个“---”只打印两次,这里也用if语句来实现
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

8.玩家下棋

打印出棋盘后,玩家就可以下棋了,那下棋的函数要怎么写?通过刚刚的棋盘初始化,我们知道二维数组中存放的全是空格,下棋时输入棋盘的坐标找到数组元素,如果该元素为空格,则用作为棋子的字符赋值给该数组元素;如果数组元素不是空格,提醒玩家格子被占用,请重新选择坐标。

//玩家下的棋用'*'表示
void PlayerMove(char board[ROW][COL], int row, int col)
{
	
	//下棋前判断是否在棋盘范围内
	while (1)//设置死循环
	{
		int i = 0;
		int j = 0;
		printf("玩家输入坐标 > ");
		scanf("%d %d", &i, &j);
		if (i > 0 && i <= ROW && j > 0 && j <= COL)
		{
			//下棋前判断格子是否被占用
			//假设我们输入2 2,表示要在2行2列下棋,但数组的下标从0开始
			//所以这里对输入的值减1才能正确访问数组
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = '*';
				break;//玩家下棋的操作成功时跳出死循环
			}
			else
			{
				printf("格子被占用,请重新输入\n");
			}
			
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}

9.电脑下棋

 这里让电脑产生两个随机数作为数组的行与列(用rand函数产生随机数,其用法我在之前的博客详细讲解过,想具体查看可以点击下方链接【c语言】rand,srand讲解(输出一个随机数)_.SacaJawea的博客-CSDN博客_输出随机数),但也要对坐标进行判断,看坐标是否被占用,而产生的随机数范围在0~8,所以不用对随机数减1

//电脑下棋用'#'表示
void ComMove(char board[ROW][COL], int row, int col)
{
	//下棋的逻辑与玩家下棋类似
	printf("电脑下棋\n");
	while (1)
	{
		int i = rand() % ROW;
		int j = rand() % COL;
		//由于产生的是随机数,判断是否非法
		if (i >= 0 && i < ROW && j >= 0 && j < COL)
		{
			//判断是否被占用
			if (board[i][j] == ' ')
			{
				board[i][j] = '#';
				break;
			}
		}
	}
}

10.判断输赢

玩家或电脑每下一步棋后,都要对输赢进行判断,当三个相同棋子连成线表示一方获得胜利(当然我们还要确保棋子不是空格),判断哪一方胜利我们可以用if语句写,除了电脑与玩家胜利,还有谁都没赢的情况,在棋盘没下满时我们要继续游戏,棋盘下满时,游戏平局同时也结束游戏。

这里一共四种情况

'#' --- 电脑胜利

'*' --- 玩家胜利

'C' --- 游戏继续

'p' --- 游戏平局

将函数的返回类型设置成char,每种情况我们都返回一种字符,根据返回的字符判断游戏该怎么进行

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < ROW; i++)
	{
		//行的判断
		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
			return board[0][i];
	}
	for (i = 0; i < COL; i++)
	{
		//列的判断
		if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[0][i] != ' ')
			return board[i][0];
	}
	//对角线判断
	if (board[0][0] == board[1][1] && board[2][2] == board[0][0] && board[0][0] != ' ')
	{
		return board[0][0];
	}
	else if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	else
	{
		return 'C';
	}

	//当没人分出胜负,并且棋盘下满的情况
	if (is_full == 0)
	{
		return 'Q';
	}
}
//判断棋盘是否下满的函数
int is_full(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; j++)
		{
			if (board[i][j] == ' ');
			//如果没下满返回1
			return 1;
		}
	}
	//没有下满返回0
	return 0;
}

11.游戏完整代码展示

11.1 game.h 游戏头文件

#pragma once

#include<stdio.h>
#include<Windows.h>

//棋盘大小数据
#define ROW 3
#define COL 3

//打印游戏菜单
void menu();

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

//展示棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);

//电脑下棋
void ComMove(char board[ROW][COL], int row, int col);

//判断棋盘是否被占满
int is_full(char board[ROW][COL], int row, int col);

//判断哪一方赢
char is_win(char board[ROW][COL], int row, int col);

11.2 game.c 游戏逻辑实现

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//游戏菜单
void menu()
{
	printf("***********************\n");
	printf("****  1.开始游戏   ****\n");
	printf("****  0.退出游戏   ****\n");
	printf("***********************\n");
}


//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
	//用两层for循环遍历数组,把数组的每个元素初始化为空格
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}


         
//展示棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	//同样是用两层for循环打印
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			//一行中打印空格、字符、空格
			printf(" %c ",board[i][j]);
			//if语句限制'|'出现次数
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");//打印完一行记得要换行
		//三个---只打印两次,这里也用if语句来实现
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}



//玩家下棋
//玩家下的棋用'*'表示
void PlayerMove(char board[ROW][COL], int row, int col)
{
	
	//下棋前判断是否在棋盘范围内
	while (1)//设置死循环
	{
		int i = 0;
		int j = 0;
		printf("玩家输入坐标 > ");
		scanf("%d %d", &i, &j);
		if (i > 0 && i <= ROW && j > 0 && j <= COL)
		{
			//下棋前判断格子是否被占用
			//假设我们输入2 2,表示要在2行2列下棋,但数组的下标从0开始
			//所以这里对输入的值减1才能正确访问数组
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = '*';
				break;//玩家下棋的操作成功时跳出死循环
			}
			else
			{
				printf("格子被占用,请重新输入\n");
			}
			
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}



//电脑下棋
//电脑下棋用'#'表示
void ComMove(char board[ROW][COL], int row, int col)
{
	//下棋的逻辑与玩家下棋类似
	printf("电脑下棋\n");
	while (1)
	{
		int i = rand() % ROW;
		int j = rand() % COL;
		//由于产生的是随机数,判断是否非法
		if (i >= 0 && i < ROW && j >= 0 && j < COL)
		{
			//判断是否被占用
			if (board[i][j] == ' ')
			{
				board[i][j] = '#';
				break;
			}
		}
	}
}

//判断棋盘是否下满的函数
int is_full(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; j++)
		{
			if (board[i][j] == ' ');
			//如果没下满返回1
			return 1;
		}
	}
	//没有下满返回0
	return 0;
}


//判断哪一方赢
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < ROW; i++)
	{
		//行的判断
		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
			return board[0][i];
	}
	for (i = 0; i < COL; i++)
	{
		//列的判断
		if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[0][i] != ' ')
			return board[i][0];
	}
	//对角线判断
	if (board[0][0] == board[1][1] && board[2][2] == board[0][0] && board[0][0] != ' ')
	{
		return board[0][0];
	}
	else if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	else
	{
		return 'C';
	}

	//当没人分出胜负,并且棋盘下满的情况
	if (is_full == 0)//根据返回值判断棋盘是否下满
	{
		return 'Q';
	}
}

11.3 程序运行逻辑 

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void game()
{
	char board[ROW][COL];//棋盘的创建
	InitBoard(board, ROW, COL);//初始化棋盘
	DisplayBoard(board, ROW, COL);//给玩家展示棋盘
	char con = 0;//判断游戏是否继续的变量
	
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋
		DisplayBoard(board, ROW, COL);//下完棋后展示棋盘

		//每一次下完棋后都要判断双方谁赢
		con = is_win(board, ROW, COL);//该函数会返回我所列出的四种情况所对应的四种字符
		if (con != 'C')//根据is_win函数的返回值决定游戏是否继续
		{
			break;
		}


		ComMove(board, ROW, COL);//电脑下棋
		DisplayBoard(board, ROW, COL);//展示棋盘

		//每次下完棋的判断
		con = is_win(board, ROW, COL);
		if (con != 'C')
		{
			break;
		}
	}//注意我设置了一个死循环
	//如果is_win函数返回继续游戏的字符,重复下棋的函数,
	//下棋的动作会一直循环直到返回其他字符,跳出循环

	//根据返回值打印游戏结果
	if (con == '*')
	{
		printf("玩家获胜\n");
	}
	else if (con == '#')
	{
		printf("电脑获胜\n");
	}
	else if(con == 'Q')
	{
		printf("平局\n");
	}
}

void test()
{
	int input = 0;
	do
	{
		menu();//打印菜单,让玩家选择是否开始游戏
		printf("请选择 > 1/0 \n");
		scanf("%d", &input);//input为是否游戏的判断依据
		switch (input)//switch语句进入不同分支
		{
		case 1:
			game();//game函数实现游戏过程的逻辑
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);//循环,让玩家决定是否继续游戏
}

int main()
{
	srand((unsigned int)time(NULL));//设置随机数种子
	test();

	return 0;
}

最后还要一个小小的注意点,就是我们可以把需要的头文件都放再game.h中,之后的源文件就只需要引用game.h啦(引用自己的源文件我们习惯用" "双引号,c语言提供的源文件我们用<> 单括号) 。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值