用C语言编写三子棋小游戏(电脑随机下版)

主程序梳理

主干思路

一个三子棋游戏首先需要的是一个棋盘,然后这个棋盘要被玩家看到,接下来是玩家行动,判断一次输赢,电脑行动判断一次输赢,以此往复。这样就可以保证程序能正常结束,不会出现棋盘占满了还是没有结束这种事。

注意:在判断输赢中不止要判断玩家还是电脑胜利,还要判断是否出现平局,防止出现死循环。

在这一次的游戏编写中行数比较多,所以我们添加一个头文件(.h),在头文件中放入各种函数的初始化,这样在主程序中我们就只用包含这个头文件就可以使用各种函数了。

头文件设计如下

#pragma once

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

#define ROW 3
#define COL 3

//初始化棋盘
void Initinalboard(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 Computermove(char board[ROW][COL], int row, int col);

//判断输赢
void Judge(char board[ROW][COL], int row, int col);

需函数做的事

棋盘初始化

初始化数组

在还没有开始游戏之前,我们让数组中的所有元素都等于‘ ’,一个一个赋值的话效率太低了,所以我们直接使用两个for循环来解决这个问题,代码如下

void Initinalboard(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++)
			board[i][j] = ' ';
	}
}

 注意:1.这一段代码中的两个变量ij都是局部变量,一旦出了这个函数就无法使用了,所以后续函数中可以将变量名再次起名为ij。

2.当两个for循环嵌套的时候,先进入第一个循环然后再进入第二个循环,执行完一遍第二个循环后,如果还满足第一个循环就会再执行一遍第二个循环,j变量也会重新变为0。

建立棋盘的基本形状

建立棋盘的基本形状相对比较简单,只用我们先打印数据行再打印分隔行就行了。行和列的初始化在头文件中就已经完成。既然已经初始化好了棋盘,那么,接下来就是要将棋盘打印出来,让玩家能看到棋盘。实现这一目的的代码如下

void Dispalyboard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		//先定义好数据
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		//再打印分段
		if(i < row - 1)
			printf("----|----|----\n");
	}
}

 但这个代码有一个不足之处——当我们更改row的值的时候不能继续将棋盘扩充为正方型,而是一个三列的长方形。效果如下

为了使程序更严谨和方便,我们将代码进一步完善如下

void Displayboard(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++);
		{
			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");
	}
}

 效果如下(将ROW改为5)

 

 这使得后续修改更方便,也使代码更严谨。

对于玩家

选择游戏开始

要开始游戏了,我们先呈现出一个让玩家明白有什么操作的菜单,这一函数也要在头文件中包含定义。代码如下

void menu()
{
	printf("**************************\n");
	printf("*******  1. game   *******\n");
	printf("*******  2. exit   *******\n");
	printf("**************************\n");
}

给出了菜单也要让玩家能够进行选择,上代码

int main()
{
	int input = 0;
	do
	{
again:
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			game();
			break;
		case 2:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			goto again;
		}
		break;
	} while (input);
	return 0;

 这样的代码可以让玩家出现逆反心理时(比如非要选一个3试试)让代码回到初始位置再让玩家选择,选择do循环的原因是do循环至少执行一遍,完美满足我们的需要。

在选择开始游戏之后棋盘出现在我们的电脑上,我们也要让玩家开始落子和电脑进行博弈了

如何落子

在最开始编译程序的时候我们就初始化了一个数组,接下来就是数组发挥作用的时候了,我们将空缺出来的,被玩家选中的数组元素(”空格“)更改为一个特定的玩家棋子(*),这样就算落下一子了。实现代码如下

void Playermove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("请玩家落子:\n");
	
	while (1)
	{
		scanf("%d%d", &x, &y);
		if (x > 0 && x <= row && y >0 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("这一格已被占用,请选择别的格");

			}
		}
		else 
		{
			printf("您选的格子不存在,请重新选择\n");
		}
	}
}

检测落子位是否被占用

实现检测的代码也在上面一段中,所谓检测其实就是检查所选中的数组元素是否为空,如果为空,那么就进行落子操作,如果不为空,那就说已被占用。

注意:1.玩家不是程序员所以不会知道数组下标是从0开始的,要将这一细节考虑进我们写的代码中,也就是将玩家输入的横竖坐标分别-1,这样就可以让代码正常运行了。

2.玩家有可能输入超出数组设置好的长宽,所以在执行代码前要先检验玩家输入的坐标是否在合理范围内。

对于电脑

如何落子

电脑的落子和玩家很相似,只是少了一步输入,并且不用对横竖坐标分别-1。代码如下

void Computermove(char board[ROW][COL], int row, int col)
{
	printf("电脑落子\n");

	while (1)
	{
		int x = 0;
		int y = 0;

		x = rand() & row;
		y = rand() & col;
		if (board[x - 1][y - 1] == ' ')
		{
			board[x - 1][y - 1] = '#';
			break;
		}
	}
}

 由于我的技术目前有限,所以我只能做到让电脑随机落子

随机生成数字一般用时间戳来实现。要用时间戳就要包含头文件<time.h>

并且在main函数前列写上如下代码

srand((unsigned int)time(NULL));

这会使生成的数完全随机,而不是之前的伪随机。 

检测落子位是否被占用

实现这一目的的代码就是上上条代码中的判断语句,它的目的就是让数组中的空变为电脑落下的子。

判断输赢,结束游戏

最后终于到了判断谁输谁赢的时间,代码如下

//玩家赢“*”
//电脑赢“#”
//平局“P”
//继续游戏“Q”

char Judge(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (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 (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//判断一竖行是否一样
		{
			return board[0][i];//返回一样的符号
		}
	}

	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')//判断\是否一样
	{
		return board[1][1];//返回一样的符号
	}

	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')//判断/是否一样
	{
		return board[1][1];//返回一样的符号
	}

	//判断平局
	if (Isfull(board, row, col))
	{
		return 'P';
	}
	return 'Q';
}

 用for循环代替一部分重复的劳动,我们列出四种胜利的条件并进行判断。

注意:每当玩家或电脑走一步之后就要进行一次判断,这样才不会出现漏判。

感谢阅读,求一个赞

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木易杨MYY

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值