数组的应用实例1—三子棋

本文描述了一个简单的命令选择界面和棋类游戏的初步设计,包括玩家和电脑的交互、棋盘初始化、落子操作、判断输赢以及游戏循环。通过switch和while/dowhile结构实现游戏流程。
摘要由CSDN通过智能技术生成

整体设计思路:

1. 程序一开始需要展示一个选择界面,根据玩家选择决定后续执行。这里假设1是开始游戏,0是退出程序,其他选择提示错误选项,即创建一个input变量接收玩家选项。此功能可以使用switch语句来实现。

除此之外,这个游戏可以重复玩,一旦结束又回到最开始的选择界面。所以将这个switch语句放到do while循环中。这样不仅可以实现一开始展示界面,还能反复玩。同样这个do while循环也使用玩家输入作为条件。

2. 玩家和电脑下棋意味落子需要被保存,所以创建一个3*3的数组( char board[ROW][COL] = { 0 }; )保存落子。玩家落子为*,电脑落子为#。为方便后续更改棋盘大小,这里在头文件game.h中使用#define来定义行和列。如果有改动,只需在game.h中修改即可。

3. 游戏开始,显示棋盘,这里需要一个显示棋盘的函数( DisplayBoard(board, ROW, COL); )。但因为还没有任何落子,所以棋盘是空的,需要用空格来暂时占位,由此引出初始化棋盘函数( InitBoard(board, ROW, COL); )。

4. 下棋时,玩家落子一次,判断输赢,显示棋盘;电脑落子一次,判断输赢,显示棋盘。一直循环,直到某一方有3个相同棋子连成线即获得胜利。否则平局。所以需要以下函数来实现:

玩家下棋函数:PlayerMove(board, ROW, COL);

电脑下棋函数:ComputerMove(board, ROW, COL);

判断输赢函数:IsWin(board, ROW, COL);

下方为test.c文件,用于测试游戏逻辑

#include "game.h"

//test.c-测试游戏逻辑
//game.c-游戏代码实现
//game.h-游戏代码的声明(函数声明,符号定义)

//此为test.c文件

void menu()
{
	printf("*************************\n");
	printf("*****    1. play    *****\n");
	printf("*****    0. exit    *****\n");
	printf("*************************\n");
}

void game()
{
	char board[ROW][COL] = { 0 };//创建3*3的二维数组(即棋盘)
	int ret = 0;//设置一个返回值,判断输赢
	
	//本质是让空格来填充数组,在没有落子的时候也能保证棋盘对齐
	InitBoard(board, ROW, COL);//初始化棋盘函数
	DisplayBoard(board, ROW, COL);//显示棋盘

	while (1)//下棋的过程,直到出结果(玩家赢或电脑赢或平局)停止循环
	{
		PlayerMove(board, ROW, COL);//玩家下棋后需要判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')//不等于C说明游戏不再继续,故跳出此循环
		{
			break;
		}	
		DisplayBoard(board, ROW, COL);//打印棋盘也需要将数组的信息打印
		
		ComputerMove(board, ROW, COL);//电脑下棋后需要判断输赢
		ret = IsWin(board, ROW, COL);//判断输赢本质是查看字符
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);			
	}
	//游戏不在继续后就要判断胜负或平局
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}
	DisplayBoard(board, ROW, COL);
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误,重新选择:>\n");
			break;
		}
	} while (input);
	return 0;
}

下方为game.h头文件,用于游戏代码的声明(函数声明,符号定义)

#include <stdio.h> //两个.c文件都需要这个头文件,并且两个.c文件都包含了这个game.h头文件
#include <stdlib.h>
#include <time.h>

//为了以后方便改成五子棋或其他,所以定义两个常量
//否则每次都要在多个地方修改参数
#define ROW 3
#define COL 3
//初始化棋盘
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 ComputerMove(char board[ROW][COL], int row, int col);

//状态总共4种:
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char	board[ROW][COL], int row, int col);

game.c中各函数实现

1. 初始化函数

就是利用嵌套循环遍历二维数组每个元素,并初始化他们为空格字符。

//先让落子的地方用空格占位,防止后期棋盘不对齐
void InitBoard(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] = ' ';
		}
	}
}

2. 显示棋盘函数

棋盘分为两部分,第一部分为落子的部分,第二部分为分割行。

第一个函数是较为简单的实现。只需要一个循环,每一次循环打印一行。但这种方法有个弊端,如果棋盘大小有改动,这个函数也需要改。

第二个函数是将棋盘的两个部分进一步细分,将每一行的内容再次划分,每一次循环只打印一个格子。

//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 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");
//	}
//}

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	//将棋盘分解为两部分
	//第一部分:(   |   |   ),这部分循环3次
	//第二部分:(---|---|---),这部分循环2次,最后一行不打印
	//所以总的(最外层循环)打印3行,内部循环通过条件控制少打印一行
	int i = 0;

	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			//printf(" %c | %c | %c \n")
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");


		if (i < row - 1)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				//printf("---|---|---\n")
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}

	}
}

3. 玩家下棋函数

首先,接收玩家输入的坐标,接着判断坐标是否合法(即是否在棋盘内),之后继续判断该位置是否下过棋。重复上去步骤,直到玩家正确落子才跳出循环。

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 (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//再查看坐标是否被占用
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;//只有当玩家正确落子才跳出循环
			}
			else
			{
				printf("坐标被占用,不能下棋,重新输入:>\n");
			}
		}
		else
		{
			printf("非法坐标,重新输入:>\n");
		}
	}
}

4. 电脑下棋函数

这里简单设置电脑随机下棋,所以需要一个随机值。在设置随机值之前需要借助时间戳设置随机数生成起点,这个起点只需要设置一次,所以放在main函数中。

将随机数模上行数和列数(值都为3)就能得到余数0~2。反复生成随机坐标,直到此坐标的位置是空格电脑才下棋。

void ComputerMove(char board[ROW][COL], int row, int col)
{
	//电脑也需要横纵坐标,但是坐标是0~2随机值(0~2是数组下标)
	//只有找到了空格才下棋
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	
	while (1)//对生成的随机横纵坐标判断,直到找到空格才下棋
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}

}

5. 判断输赢函数和判断棋盘是否被下满 函数

判断输赢即行、列、对角线都是同一种字符,这里不要明确是*(玩家落子)还是#(电脑落子),只需返回数组里保存的字符即可。

如果没有人获胜,那么就判断是否为平局,由此引出判断棋盘是否下满函数。判断是否下满,只需遍历每个数组的字符,如果没有空格即为满。

//满了返回1
//不满返回0
//此函数查看棋盘是否满了,即数组是否还有空格
int IsFull(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] == ' ')
				return 0;
		}
	}
	return 1;
}

char IsWin(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//判断行:第1格和第2格相等,并且第2格和第3格相等,并且任意一格不为空格
		//不需再判断里面的字符是玩家落子还是电脑落子,直接返回里面的字符即可
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
			return board[i][1];
	}

	int j = 0;
	for (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
			return board[1][j];
	}

	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];

	//上方为出现了胜负,如果没有人赢,则进入下面平局代码
	//平局:棋盘放满,但没有人赢
	//还有空格返回0(棋盘不满),没有空格返回1(棋盘满了)
	//返回0,if条件为假,就返回C,意味着继续
	//返回1,if条件为真,就返回Q,意味着结束
	if (IsFull(board, row, col))//此为调用棋盘是否为满函数,并将棋盘数组传参
	{
		return 'Q';
	}
	else
	{
		return 'C';
	}
		
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值