C语言 三子棋,超详细

​​​

 


目录

创建源文件以及头文件

创建文件

大体逻辑

创建以及初始化棋盘

创建棋盘

棋盘初始化

打印棋盘

意外的插曲

玩家下棋

电脑下棋

判断输赢

判断行:

判断列:

判断对角线:

判断平局:

优化一下(可能有点鸡肋)

上代码


创建源文件以及头文件

创建文件

创建源文件时建议创建2个源文件,一个用来测试整个游戏的逻辑,另一个用来进行游戏的实现。

还需要创建一个头文件用来放函数的声明

大体逻辑

对于内部函数,先写个雏形,即游戏菜单,运行逻辑,游戏逻辑

 

创建以及初始化棋盘

创建棋盘

(此时应该考虑,目前所需棋盘为一个3行3列的二维数组,如果以后需要10行10列时又应该怎么做?)

所以可以在头文件中对行和列进行定义,方便以后的更改

 

棋盘初始化

在棋盘定义好之后就该将棋盘全部初始化为空格,此时为一个具体的步骤,可以单独写一个函数来执行,这里,编写了Intboard函数来执行这一步骤:
这个步骤属于游戏过程的实现,将它放在了game.c文件之中。
别忘了在头文件中包含函数的声明!!!

打印棋盘

接下来就该打印棋盘了,同理可以创建一个函数来执行这一步骤。

首先要对我们需要的棋盘样式来进行分析

 放在game.c 中也别忘了在头文件中对函数的声明!!!

先展示下棋盘吧

意外的插曲

这里需要插一句,我写到这里在运行的时候出现了这样的警告(数组board周围的栈区损坏)

当时我感觉是发生了数组越界,但着实是没有发现问题的出处,在多次查找资料,询问大佬,以及自己的不懈努力的调试之下,终于发现了问题的原因(在初始化棋盘为空格的时候,第一个for循环语句之后有一个分号没有删除)哎,真是虚惊一场

玩家下棋

现在棋盘也已经打印好了,接下来就该轮到我们伟大的玩家上场表演了!

首先也要建立函数哇,棋盘以及棋盘的行列作为参数,别忘了在头文件中声明!!!

 

然后开始让玩家下棋的时候我们需要考虑到玩家下的位置是否在棋盘内部?是否该位置已经有棋子了?

以下是最后结果(这里值得注意的是if语句判断不可以写作

((0<=x<=row)&&(0<=y<=col) )

这里就是利用玩家输入的坐标将对应的二维数组的元素改为玩家的棋子

电脑下棋

玩家下棋以后,就该电脑来下棋了,

如果希望电脑可以智能的堵棋等操作那么涉及的代码会有些复杂,这里我们先利用随机值来运行

这里就利用到了rand函数,但是rand又需要随机种子,所以我们就要用到时间戳及srand((unsigned int)time(NULL))这个每个程序调用一次就好,所以把他放在了运行逻辑中

在strand中调用time函数,给time函数定义返回值为空指针NUll,返回类型给一个unsigned int

这里使用了时间函数需要包含头文件,我们把它放在头文件game.h中

头文件需要包含#include<stdlib.h>以及#include <time.h>

做完上面的步骤就实现了对 电脑下棋的随机值

(但是还需要考虑的是对于电脑下棋时位置的判断,该位置是否已经有棋子了,如果有还需要再次赋予随机值)所以就用到了死循环

这样就实现了电脑的下棋,注意电脑下棋之后再次打印一遍棋盘哦,具体原理同上!

判断输赢

在判断输赢之前需要电脑和玩家一直循环下棋,但每下一步都要判断输赢哦!

先放进去再说。 

电脑下棋和玩家下棋都写好了,那剩下的就是判断输赢以及平局了,那就写个判断输赢的函数吧

既然要判断输赢,就要涉及到返回值,和返回类型

 这里为什么会想到' * ',' # '来作为返回值呢?

因为如果判断每一行均为某一方的棋子,就可以直接用该方的棋子来代替返回值,省去了赋值的麻烦。就像是如果A是裁判,A要告诉B是玩家赢了的时候,是A给B说’1‘,然后B再去想:“1就是玩家赢了”,这样方便;还是A直接把玩家的棋子给了B方便呢?

所以这样就确定了判断函数的返回值以及返回类型。接下来就该考虑应该怎样判断了:

判断行:

只要出现某一行中的三个元素相等且都不等于空格,就可以判断其中一方胜利,至于返回值,我们可以直接用相等的那一方的棋子来代替,省去了重新赋值的麻烦,

判断列:

与判断行同理。

判断对角线:

只需要判断主对角线和次对角线的棋子是否相等且都不等于空格即可。

判断平局:

可以设置循环,逐个元素进行扫描,如果扫描到空格,则直接判断为非平局(继续游戏),

当扫描到9个格子均为棋子时,判断为平局,这里需要利用变量来保存每次扫描到的棋子总数,设置为a。

所以最后的判断可以这么写

 既然有了判断,有了返回值,就该对返回值做一系列的处理了,这里我们创建了ret变量用来接收判断函数的返回值,并对ret的值做判断。因为这里整体都在死循环中放着,所以对于返回值C就不需要太纠结,继续运行下去就好。

 那判断的位置应该放在哪呢?当然是每次下棋打印完棋盘之后,要做到每发生一次变化都要判断一次

那么写道这里,整个三子棋的流程基本都完成了 ,实现了!

先恭喜各位的成功!!!

那么可不可以对代码进行再一次优化呢?(或许有些鸡肋,还要看各位观众老爷的心情!!!)

优化一下(可能有点鸡肋)

1.打印棋盘的时候可不可以顺便吧坐标打印了?

这里做了细微的变化,在每次打印之前先打印行标,然后将列标划分到每行打印的第一个元素中,整体分为打印第一个元素,打印中间元素和打印末尾元素。

2.在玩游戏的时候,可不可以适当的清屏?

这里需要用到system用来执行系统命令

1)可以考虑在玩家输入完菜单选项之后进行一次清理屏幕

2)可以考虑在玩家与电脑每回合清理屏幕,并保留最近一次的棋盘数据

3)在游戏结束的时候清理之前的屏幕,并打印最终结果的棋盘

既然各位都看到这里了,我也不藏着掖着了

上代码

这里是关于游戏逻辑过程中的全部函数,有需要自提,不敢说特别有用,给各位应应急还是可以的!

//初始化棋盘为全空格
void Intboard(char board[Row][Col], int row, int col)
{
	int r = 0;
	for (r = 0; r < row; r++)
	{
		int c = 0;
		for (c = 0; c < col; c++)
		{
			board[r][c] = ' ';
		}
	}
}

//打印棋盘
void printboard(char board[Row][Col],int row,int col)
{
	int r = 0;//行
	printf("    0 | 1 | 2 \n");
	for (r = 0; r < Row; r++)//打印的行数
	{
		int c = 0;//列
		for (c = 0; c < Col; c++)//用列来确定每一行打印多少个
		{
			//打印坐标
			if (c == 0)//打印每行的第一个元素
			{
				printf(" %d ", r);
				printf(" %c |", board[r][c]);
				c++;
			}
			if (c > 0 &&c < Col - 1)
				printf(" %c |", board[r][c]);//打印每行的中间元素
			else
				printf(" %c \n", board[r][c]);//打印最后一个元素
		}
		if (r < Row - 1)//最后一行不打印分割线
		{
			//打印分割线
			for (c = 0; c < Col; c++)
			{
				if (c == 0)
				{
					printf("   ---|");
					c++;
				}
				if (c > 0 && c < Col - 1)
					printf("---|");
				else
					printf("---\n");
			}
		}
	}
}

//玩家下棋
void playermove(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	while (1)
	{
		printf("请玩家输入坐标>\n");
		scanf("%d %d", &x, &y);
		if ((0 <= x && x<=row) && (0 <= y && y <= col))
		{
			if (board[x][y]== ' ')
			{
				board[x][y] = '*';
				break;
			}
			else
				printf("坐标被占用,请重新输入!\n");
		}
		else
			printf("输入内容不符合规定,请重新输入!\n");
	}
	
}

//系统下棋
void computmove(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;
		}
	}
}

// 判断输赢
char  win_game(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	//判断行
	for (x = 0; x < Row; x++)
	{
		if (board[x][0] == board[x][1] && board[x][1] == board[x][2] && board[x][2] != ' ')
			return board[x][0];
	}
	//判断列
	for (x = 0; x < Col; x++)
	{
		if (board[0][x] == board[1][x] && board[1][x] == board[2][x] && board[2][x] != ' ')
			return board[0][x];
	}
	//判断对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ')
		return board[0][0];
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
		return board[0][2];
	//判断平局
	int a = 0;
	for (x = 0; x < Row; x++)
	{
		for (y = 0; y < Col; y++)
		{
			if (board[x][y] != ' ')
				a++;
			else if (board[x][y] == ' ')
				return 'C';
		}
		if (a == 9)
			return 'Q';
	}

}

如果需要全部的代码

DHRS: 个人仓库,有需要的自提。https://gitee.com/drink-more-hot-water-a/dhrs.git

 感谢各位观众老爷看到这里,如果感觉对你有帮助的话,还希望多多点赞支持!!!

如果对以上有什么意见之类的,还希望大家指出!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值