如何实现三子棋

1 什么是三子棋

率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了


2 明白棋盘中的元素构成

2.1 实现:棋盘中的线路

对此我们使用的是键盘中的竖线:’|‘,以及键盘中的下划线:’_‘

最终我们想要实现的效果是怎样的呢?(如下图所示)

那么我们应该如何使用代码来实现呢?

首先我们要棋盘背后的真实情况:(如下图所示)

看似3*3的棋盘,实际上我们所需要的是6*11的棋盘,其中☆所占用的位置用来存放棋子

而存放棋子,我们使用二维数组arr[3][3]

如何将我们的想法呈现在我们的眼前呢?(不考虑棋子arr[3][3]中存放的是什么)

首先需要明白每一行所包含的内容:(除开最后一行)

同样每一行的内容中(除开最后一列)的均为:

 分为两行:

    一行为:空格+棋子+空格+竖线

    一行为:下划线+下划线+下划线+竖线

最后一列:同前面的所有列,不同之处在于最后一列少一条竖线(仅此而已)

代码实现如下:(除开最后一行)

//代表第一行的内容
for (int j = 0; j < col; j++) {
    //先打印空格+棋子+空格
	printf(" %c ", arr[i][j]);
    //用以区分最后一列:最后一列没有竖线
	if (j < col - 1) {
        //打印最后的竖线
		printf("|");
	}
}

printf("\n");//一定要记得换行

//代表第二行的内容
for (int j = 0; j < col; j++) {
    //打印三条下划线
	printf("___");
    //用以区分最后一列:最后一列没有竖线
	if (j < col - 1) {
        //打印最后的竖线
		printf("|");
	}
}

而最后一行不同于其他行的地方为:第二行处(没有三条下划线)

//判断是否为最后一行
if (i < row - 1) {
	for (int j = 0; j < col; j++) {
		printf("___");
		if (j < col - 1) {
			printf("|");
		}
	}
}
//最后一行为以下代码:无三条下划线
else {
	for (int j = 0; j < col; j++) {
		printf("   ");
		if (j < col - 1) {
			printf("|");
		}
	}
}

所以对于棋盘来说:最终的代码实现为(将其封装到名为:Displ_arr函数中)

void Display_arr(char arr[ROW][COL], int row, int col) {

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

		printf("\n");

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

		printf("\n");
	}
}

2.2 实现:棋盘中的棋子

正式棋盘中使用‘×’, ‘O’两种符号来表示,而我们为了简化一点,使用‘*’, ‘#’,分别代表两种棋子

用来存放棋子的位置如上图所示:所以需要一个二维数组来存放棋子

但在最起初时,每个位置都必须是空白的,所以需要初始化

void Init_arr(char arr[ROW][COL], int row, int col) {
    //初始化,将每个位置都初始化为空格
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
            //单引号才是字符,双引号时字符串
			arr[i][j] = ' ';
		}
	}
}

那么有了初始的数组以及棋盘我们之后应该考虑什么呢?

没错就是开始下棋!


3 如何实现下棋的操作

3.1 玩家下棋

玩家下棋时需要解决两个问题,一是,如何告知电脑想要下的位置,二是,下棋时如何判断是否该位置可以下子(是否已有棋子)

问题一:如何告知电脑想要下的位置

如上面的2.2 已拥有了存放棋子的二维数组,arr[ROW][COL],此时我们只需要告知电脑我们的横纵坐标是什么即可

(但需要确定横纵坐标都符合[0,ROW)或者[0,COL))

void Player_move(char arr[ROW][COL], int row, int col) {
    //告知电脑横纵坐标
	int x = 0;
	int y = 0;
	printf("请输入下棋的坐标:\n");
	scanf("%d %d", &x, &y);
    //简单判断下,是否在可输入的坐标之内
	if (x >= 0 && x < ROW && y >= 0 && y < COL) {
		//在则将该位置的符号改为‘*’
		arr[x - 1][y - 1] = '*';
	}
    //不满足坐标范围的话,告知使用者,重新输入坐标
    else
		printf("请重新输入有效坐标:");
}

此时我们输入的是从下表为0的位置处开始的,可能程序员们觉得很正常,但是常理来说应该从1开始,所以我们此时做出一个小小的改变,让其从1开始(两处改变)

void Player_move(char arr[ROW][COL], int row, int col) {
	int x = 0;
	int y = 0;
	printf("请输入下棋的坐标:\n");
	scanf("%d %d", &x, &y);
    //此时判断条件也相应的进行了修正
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
        //将x,y两者都进行-1的操作即可
		arr[x - 1][y - 1] = '*';
	}
    //不满足坐标范围的话,告知使用者,重新输入坐标
	else
		printf("请重新输入有效坐标:");
}

问题一已经解决,此时我们需要解决问题二

问题二:下棋时如何判断是否该位置可以下子(是否已有棋子)

解决思路:在下棋时判断是否该位置为空或者判断是否已有棋子

(两种均可,我们采用前者,因为前者只需判断是否为‘ ’,后者需要判断是否为‘*’‘#’。麻烦一点)

void Player_move(char arr[ROW][COL], int row, int col) {
	int x = 0;
	int y = 0;
	printf("请输入下棋的坐标:\n");
    //为了保证一定可以下到棋子,我们采用while(1){},仅当成功下了棋子的时候跳出
	while (1) {
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
            //判断该位置是否为‘ ’
			if (arr[x - 1][y - 1] == ' ') {
				arr[x - 1][y - 1] = '*';
                //成功下棋后,直接跳出
				break;
			}
            //不为空的情况,告知使用者,重新输入坐标
			else {
				printf("该坐标已被占用,请重新输入:");
			}
		}
        //不满足坐标范围的话,告知使用者,重新输入坐标
		else
			printf("请重新输入有效坐标:");
	}
}

至此两种问题均已解决,玩家下棋已被成功实现!

并将其封装至名为:Player_move的函数中

3.2 电脑下棋

玩家下棋可以由我们来控制下棋的位置输入,那么电脑下棋由谁来进行输入呢?

没错,由电脑自主进行输入,我们采用随机值的办法来让电脑进行输入

同理,也需要解决位置是否可用的问题

void Computer_move(char arr[ROW][COL], int row, int col) {
	int x = 0;
	int y = 0;
    //同样的使用while(1)的方式,来保证电脑至少可以输入一颗棋子
	while (1) {
        //采用%的办法,将x,y的值控制在可输入的坐标范围之内
		x = rand() % row;
		y = rand() % col;
        //同样需要判断该位置是否可以下棋子
		if (arr[x][y] == ' ') {
			arr[x][y] = '#';
            //成功下棋子后,退出
			break;
		}
	}
}

至此电脑下棋已被成功实现!

并将其封装至名为:Computer_move的函数中

但下棋也应该有一个终点,及判断谁输谁赢,或者平局的情况,所以我们还应该解决判断输赢的问题


4 如何判断输赢

4.1 输赢的标志是什么

三子棋,有任何一方连成一条直线时,获得胜利,棋子填满棋盘,但并未有任意一方连成直线时,为平局

连成直线有几种方式呢?

有着横,竖,主对角线,副对角线四种情况,所以我们首先要进行四种情况的判断

但不要忘记了平局的情况,所以有五种情况的判断

char Is_win(char arr[ROW][COL], int row, int col) {
	//判断行
	for (int i = 0; i < row; i++) {
		int count = 0;
		for (int j = 0; j < col-1; j++) {
			if (arr[i][j] == arr[i][j + 1] && arr[i][j] != ' ') {
				count++;
			}
		}
		if (count == col - 1) {
			return arr[i][0];
		}
	}

	//判断列
	for (int i = 0; i < col; i++) {
		int count = 0;
		for (int j = 0; j < row - 1; j++) {
			if (arr[j][i] == arr[j + 1][i] && arr[j][i] != ' ') {
				count++;
			}
		}
		if (count == row - 1) {
			return arr[0][i];
		}
	}

	//判断主对角线
	int count1 = 0;
	for (int i = 0; i < row-1; i++) {
		if (arr[i][i] == arr[i + 1][i + 1] && arr[i][i] != ' ') {
			count1++;
		}
		if (count1 == row - 1) {
			return arr[i][i];
		}
	}

	//判断副对角线
	int count2 = 0;
	for (int i = 0; i < row - 1; i++) {
		if (arr[i][row - i] == arr[i + 1][row - 1 - i] && arr[i][row - i] != ' ') {
			count2++;
		}
		if (count2 == row - 1) {
			return arr[i][row - i];
		}
	}

	//判断平局
	if (Is_full(arr,ROW,COL) == 1) {
		return 'Q';
	}

	//继续
	return 'C';
}

以上函数我们使用char类型的返回值,平局返回‘Q’,未分胜负返回‘C’,已分胜负返回各自对应的字符‘*’或者‘#’

并将其封装至名为:Is_win的函数中

至此所有可能被应用的一系列函数已经封装结束


5 如何应用以上函数

//游戏
void game() {
	//棋盘
	char arr[ROW][COL] = { 0 };
	//初始化棋盘
	Init_arr(arr,ROW,COL);
	//打印棋盘
	Display_arr(arr, ROW, COL);

	//判断输赢
	char ret = 0;
	while (1) {
		//玩家下棋
		Player_move(arr, ROW, COL);
		Display_arr(arr, ROW, COL);
		//判断输赢
		ret = Is_win(arr, ROW, COL);
		if (ret != 'C') {
			break;
		}

		//电脑下棋
		Computer_move(arr, ROW, COL);
		Display_arr(arr, ROW, COL);
		//判断输赢
		ret = Is_win(arr, ROW, COL);
		if (ret != 'C') {
			break;
		}
	}

	if (ret == '*') {
		printf("恭喜,你赢了!\n");
	}
	else if (ret == '#') {
		printf("抱歉,你输了!\n");

	}
	else {
		printf("游戏结束,平局!\n");
	}
}

游戏的总体我们将之封装为game函数中

首先我们创建好存放棋子的二维数组,并将其初始化

初始化完成后打印棋盘,展示出来,告诉使用者棋盘的样子

然后开始游戏,进入循环,首先玩家下棋,然后电脑下棋,直至游戏结束

因为下棋子后,我们需要了解当前棋子的情况,所以我们需要每次下棋之后都要打印一下棋盘

因为游戏的结束只有在下棋子之后才能判断,所以我们可以在每次下棋之后进行判断

最后Is_win函数的返回值来判断游戏的最终结果


6 完善游戏

int main() {
    //选项
	int input = 0;
    //为了随机值处所写
	srand((unsigned int)time(NULL));
    //开始进入游戏中
	do {
		//目录
		catalog();
		//选择
		printf("请输入你的选择:>");
		scanf("%d", &input);

		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("游戏结束!");
			break;
		default:
			printf("您的输入有误,请重新输入");
			break;
		}
	} while (input);

	return 0;
}

上文中提到的目录如下文所示:

//目录
void catalog() {
	printf("#####################\n");
	printf("##### 1. Play #######\n");
	printf("##### 0. End  #######\n");
	printf("#####################\n");
}

至此游戏开发完成,可供正常使用


7 全部代码展示

game.h

 game.h

game.c

test.c

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值