【三子棋】C语言版——妈咪级讲解

大家好,今天为大家带来三子棋C语言版讲解,这个游戏看着简单,但是对于我这种新手实现起来还是有一定难度的,废话少说,直接开冲。

目录

一、事先准备

二、用户交互画面

三、三子棋棋盘的初始化和打印

四、玩家输入和电脑输入

玩家移动

电脑移动

五、判断输赢和平局

六、头文件和game函数


一、事先准备

我们要创建一个头文件和两个源文件

头文件game.h放游戏相关函数的声明

源文件test.c实现主函数,大致的框架

源文件game.c实现游戏相关函数

这样做的好处是将各复杂函数间模块化,也避免一个文件过于复杂,方便检查和相互调用。

定义三子棋棋盘的行和列,使用#define定义 ROW和COL,这里我们先定义为3,后期我们改造这个游戏为五子棋什么的只有改动ROW和COL就可以实现了。

二、用户交互画面

用户交互画面主要实现菜单mean函数的打印,供用户选择开始游戏和退出游戏。会用到do……while循环和switch选择结构。

交互画面还是比较简单的,在上一篇猜数字里就详细介绍到了,这里就不多说了。

int main()
{
	srand((unsigned int)time(NULL));//种子生成
	int input = 0;
	do
	{
		mean();
		printf("请输入选项:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:game();
			break;
		case 0:
			printf("游戏退出成功\n");
			break;
		default:printf("输入错误,请重新输入:->");
			break;
		}
	} while (input);

	return 0;
}

三、三子棋棋盘的初始化和打印

我们要来接受用户传来的值首先就要创建一个数组,并对数组中每个值赋值为空格。

这里我们创建一个char型的board数组行设为ROW,列设为COL。

然后创建一个void函数inti_board(初始化数组,将每个值初始化为空格)

这里我们将board数组作为函数参数传给inti_board,int_board用char board【ROW】【COL】接收数组,推荐使用数组的形式传参,使用指针的方式传二维数组还是比较容易出错的,不推荐。

//初始化棋盘
void inti_board(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i++)
	{
		for (j = 0; j < COL; j++)
		{
			board[i][j] = ' ';
		}
	}
}

这样就对数组进行了初始化赋值,接下来我们就要来实现打印棋盘了,棋盘的样式如下:

 这样的棋盘看着就比较像三子棋的棋盘了,接下来我们对这个棋盘进行分析:

表面上是三行,其实这个棋盘是打印三次,每次打印了两种内容的,由两种打印方式实现

1是空格、数据、空格加上竖杠,2是三个减号加上竖杠

而且当列数为3时不打印最后那个竖杠,行数为最后一行时不打印减号和竖杠

这里我们画图来分析一下:

有了分析,我们就可以很轻松的用选择和循环语句来控制打印,代码如下:

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

四、玩家输入和电脑输入

玩家移动

这时我们实现玩家移动player_move,设置一个x,y,用来接受用户输入的坐标,用*来表示用户的棋子同时我们要来进行判断两件事:

1、用户输入的坐标是不是在行列范围之内

2、用户输入的坐标原先是不是空格

这时我们就要来设置一个循环,只有上面两个条件满足了才能将那个坐标的值改动,并且跳出循环。

当然,我们还要考虑一件事,用户可能不知道数组是从零开始的,所以我们要将用户输入的坐标x,y进行减1,再放入到相对应的数组位子中去。代码实现如下:

//玩家移动
void player_move(char board[ROW][COL])
{
	//接受坐标
	int x = 0;
	int y = 0;
	while (1)                                    
	{
     printf("玩家请输入坐标->\n");
	 scanf("%d %d", &x,& y);                     //玩家输入坐标
	 if (x >= 1 && x <= 3 && y >= 1 && y <= 3)   //x和y的值在区间
	 {
		 if (board[x - 1][y - 1] == ' ')         //并且输入的位子是空格
		 {
			 board[x - 1][y - 1] = '*'; break;   //赋值退出
		 }
		 else
			 printf ("位置被占用\n");
	 }
	else 
		 printf("输入不合法");
	}
	
}

电脑移动

我们将电脑落子定为#,首先我们来思考电脑应该如何下子,电脑应该如何产生坐标,这时我们就可以想到我们猜数字游戏中用过的rand函数来让电脑产生下棋的坐标(因为本人实力有限,不能设计算法来让电脑智能下棋)电脑下子的位子相比玩家下子就易控制一些,我们无需判断他是否会下出界,我们只用%3就可以让电脑产生的值一直再0-2之间,我们只用设置循环来让电脑产生的坐标原来是个空格就行了,然后让子落下,退出循环,再打印棋盘就行了。

这时我们可以设置一点细节,使用Windows.h中的Sleep函数,设置休息时间为Sleep(300)单位为毫秒,也就是0.3秒,来让下棋显得不那么僵硬,函数具体实现如下:

//电脑移动
void computer_move(char board[ROW][COL])
{
	int x = 0;
	int y = 0;
	
	while (1)      
	{
       x = rand() % ROW;
	   y = rand() % COL;               //x,y不断判断;
		if (board[x][y] == ' ')         //判断生成的x和y的数组位子是空格
		{
			board[x][y] = '#';
			printf("电脑移动:\n");       //输出电脑在移动;
			Sleep(300);                  //假装电脑在思考
			break;                      //是空格就跳出循环
		}
	}
}

五、判断输赢和平局

首先判断输赢我们可以先思考,怎样算赢,三子棋有8种情况判为赢,即行三连,列三连,还有对角线三连。

那平局就很好想了,9个格子全不是空格。

那这里我们先来给个设定,下一步判断一次下棋状况,如果判断有一方胜利,则返回赢的情况中的一个值,如果有平局,则返回一个Q,代表和棋,如果都不满足,则返回C,游戏继续。

//判断输赢
char if_win(char board[ROW][COL])
{
	//玩家赢 返回*,电脑赢 返回#, 每次再判断平局返回Q,以上三种都没发生返回C;
	int i = 0;
	int j = 0;
	for (i = 0; i < ROW; i++)
	{
		if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][1] == board[i][2])
			return board[i][0];
		if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i])
			return board[2][i];
	}
	if (board[1][1] != ' ' && board[0][0] == board[1][1] && board[2][2] == board[1][1])
		return board[1][1];
	else if (board[1][1] != ' ' && board[2][0] == board[1][1] && board[1][1] == board[0][2])
		return board[1][1];
	else if (if_full(board))
		return 'Q';
	else 
		return 'C';
}
void print_win(int sign)
{
	if (sign == '*')
		printf("*****玩家赢*****\n");
	else if (sign == '#')
		printf("*****电脑赢*****\n");
	else
		printf("平局\n");

}

我们可以想到,只要返回的是C,游戏继续,如果不是C,那就有三种情况:玩家赢,电脑赢,和平局,这时我们来进行判断。如果有一方触动了胜利的条件,则直接将其中的一个值返回就行了,然后我们来判断是*还是#号。同样,如果触发了平局条件,我们判断是不是平局条件,如果是平局,我们输出平局。那我们要再来实现if_full函数,棋盘满的条件。

和棋就是没有空格,那我们只用每次遍历一遍就行了,实现起来还是比较简单的。

//判断平局
int if_full(char board[ROW][COL])
{
	int i = 0;
	int j = 0;
	for (i=0;i<ROW;i++)
		for (j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	return 1;
}

这样,输赢、和、继续就判断完了,我们只需要再来一个打印判断的结果的函数就行了

实现如下:

void print_win(int sign)
{
	if (sign == '*')
		printf("*****玩家赢*****\n");
	else if (sign == '#')
		printf("*****电脑赢*****\n");
	else
		printf("平局\n");

}

这样,三子棋全部的函数部分就实现完了,我们现在要做的就是将函数声明放到头文件中,这样文件间就有了关系,文件间就可以相互调用了。

六、头文件和game函数

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
//定义常量
#define ROW 3
#define COL 3
//函数部分声明

//菜单
void mean();
//游戏函数
void game();
//棋盘数组初始化
void inti_board(char board[ROW][COL]);
//打印棋盘
void print_board(char board[ROW][COL]);
//玩家输入
void player_move(char board[ROW][COL]);
//电脑移动
void computer_move(char board[ROW][COL]);
//判断输赢
char if_win(char board[ROW][COL]);
//判断平局
int  if_full(char board[ROW][COL]);
//结果打印
void print_win(int sign);

接下来我们来看一下主函数部分,看一下game函数的主要部分:

void game()
{
	char sign ;
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	inti_board(board);
	//打印棋盘
	print_board(board);
	//游戏进行
	while (1)
	{
	//玩家移动
	 player_move( board);
	 print_board(board);
	 //移动一次判断一次输赢
	 sign = if_win(board);
	 if (sign != 'C')
	 {
		 print_win(sign);
		 break;
	 }
	 //电脑移动
	 computer_move(board);
	 print_board(board);
     sign = if_win(board);
	 if (sign != 'C')
	 {
		 print_win(sign);
		 break;
	 }
	}
}

这样,三子棋游戏就实现了,总结一下,我们将游戏的函数部分、主函数放在不同的文件,这样有利于我们一步一步的调试,也便于我们分析程序,再将函数声明放在头文件中,这样巧妙的连接了两份源文件,并且减少了冗余。

大家留个赞呗~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Brant_zero2022

素材免费分享不求打赏,只求关注

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值