首页 > 其他分享 >C语言实现三子棋(N子棋)

C语言实现三子棋(N子棋)

时间:2024-09-14 22:55:17浏览次数:10  
标签:int 三子 C语言 ++ board 子棋 棋盘 col row

目录

1.游戏规则

2.游戏实现

3.游戏测试

4.代码链接

---------------------------------------------------------------------------------------------------------------------------------

1.游戏规则

        三子棋又称井字棋,一般来说是在一个3×3的棋盘中,双方执不同的棋按先后顺序依次落子,率先将棋连成连续3个(同一种棋连成一行或者一列,或者斜对角)的一方获胜,如下图所示,红方为先行方,红方率先将棋连成3子一线。

2.游戏实现

        首先我们将项目分为游戏和测试两个模块,分别创建game.h game.c和test.c三个文件,game文件用于游戏的声明和实现,test模块用于游戏的测试。

        游戏的大致逻辑如下所示:

以上转换为代码:

//游戏测试模块
void test() {
	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);
}

//实现一个三子棋的游戏
int main() {
	test();
	return 0;
}

2.1打印菜单

        打印游戏菜单menu(),提示用户输入,1代表开始游戏,0表示推出游戏

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

2.2获取用户输入

        获取用户输入,根据输入判断是否执行游戏模块。

2.3执行游戏程序

        当用户输入1时,进入游戏模块game()。游戏执行后,应该先打印一张空的棋盘,用户输入坐标后,重新打印已经落子后的棋盘,接着电脑下棋,再打印棋盘,依次类推。同时,在一方落子后,需要判断棋面是哪一方赢,或者平局,或者棋还未下完,根据棋面状态决定继续下棋还是结束下棋。游戏的执行逻辑如下:

 2.3.1打印棋盘

        为了能够打印棋盘,首先创建棋盘并进行初始化,为了能够更灵活的复用代码,在game.h头文件中定义两个符号ROW和COL来分别代表棋盘数组的行和列,后续要改变棋盘的大小,只需要改变这两个量的值。

#define ROW 3
#define COL 3
//创建棋盘
char board[ROW][COL] = { 0 };

//初始化棋盘
init_board(board, ROW, COL);

//打印棋盘
print_board(board, ROW, COL);

        为了能在终端上看的出来棋盘,首先将数组初始化为空格,即' ',并在相应的行和列打印横竖边界,打印的形式如下图所示:

        对应的代码:

//初始化棋盘
void init_board(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] = ' ';
		}
	}
}

//打印棋盘
void print_board(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");
	}

}

        终端打印效果:

2.3.2玩家下棋

        能够顺利打印棋盘后,接下来是玩家进行下棋,首先获取玩家输入的坐标信息,得到输入坐标后需要判断坐标值是否有效,例如是否取值在设定的棋盘坐标大小之内,坐标判定有效后,仍需判定坐标所表示的地方是否已经下过棋,若下过棋,需要提示用户重新输入坐标,若没有,则将对应位置的内容改为玩家所下的棋,这里用字符 * 表示玩家下棋,字符 # 表示电脑下棋, 因为数组的下标从0开始,而玩家默认坐标是从1开始的,所以应该将输入的横纵坐标都减1后,才是需要更改的棋盘坐标。玩家下棋逻辑如下:

        代码: 

void player_turn(char board[ROW][COL], int row, int col) {
	int x = 0;
	int y = 0;
	while (1) {
		printf("玩家下棋:>");
		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");
		}
	}
}
2.3.3 电脑下棋

        电脑下棋同理,通过获取0~棋盘大小的随机值,例如x=rand()%row,y=rand()%col,若row为3,即x的取值范围为0~2(注意,调用rand()函数我们需要给其指定sand(),即随机值起点,我们可以指定从当前时间开始,即srand((unsigned int)time(NULL));同样判断坐标值是否已经不是空格,若不是,修改对应位置值为字符 #。

2.3.4判断输赢

        双方各自下完棋后,需要打印更改后的棋盘,然后判断输赢。这里用字符 * 表示玩家赢,字符 # 表示电脑赢,字符 Q 表示平局,字符 C 表示棋未要玩,继续下棋,根据判断的结果返回相应的字符。

        一方赢的条件为3行或3列或斜对角连成3个相同形状的符号,例如可以先遍历棋盘数组,获取一行中的元素个数,将一行中的元素个数存放在一个arr[]数组中,数组的大小就是棋盘数组一行元素的个数,即棋盘数组的列数,存放入arr[]后再判断其中元素是否相等,若相等,再判断是字符 * 还是字符 #,若不相等,同理再获取每一列与对角元素,并进行比较。

        若以上判断没有结果,再判断棋盘是否已经被下满,即棋盘数组元素已经没有空格字符了,若被下满,则表示平局,返回字符 Q,若没有,则继续下棋。

//判断数组内元素是否相等
int is_equal(char arr[], int sz) {
	char ret = arr[0];
	for (int i = 1; i < sz; i++) {
		if (ret != arr[i]) {
			return 0;
		}
	}
	return 1;
}

//判断输赢
char is_win(char board[ROW][COL], int row, int col) {
	//获取每一行数据,判断是否相等
	for (int i = 0; i < row; i++) {
		char arr1[COL] = { 0 };
		for (int j = 0; j < col; j++) {
			arr1[j] = board[i][j];
		}
		if (1 == is_equal(arr1, col)) {
			if (arr1[0] == '*') {
				return '*';
			}
			else if (arr1[0] == '#') {
				return '#';
			}
		}
	}
	//获取每一列数据,判断是否相等
	for (int i = 0; i < row; i++) {
		char arr1[ROW] = { 0 };
		for (int j = 0; j < col; j++) {
			arr1[j] = board[j][i];
		}
		if (1 == is_equal(arr1, row)) {
			if (arr1[0] == '*') {
				return '*';
			}
			else if (arr1[0] == '#') {
				return '#';
			}
		}
	}
	//获取斜对角数据,判断是否相等,行和列需相等
	if (row == col) {
		char arr2[ROW] = { 0 };
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < row; j++) {
				if (i == j) {
					arr2[i] = board[i][j];
				}
			}
		}
		if (1 == is_equal(arr2, row)) {
			if (arr2[0] == '*') {
				return '*';
			}
			else if (arr2[0] == '#') {
				return '#';
			}
		}

		char arr3[ROW] = { 0 };
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < row; j++) {
				if (i + j == row - 1) {
					arr3[i] = board[i][j];
				}
			}
		}
		if (1 == is_equal(arr3, row)) {
			if (arr3[0] == '*') {
				return '*';
			}
			else if (arr3[0] == '#') {
				return '#';
			}
		}
	}

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (board[i][j] == ' ') {
				return 'C';
			}
		}
	}
	return 'Q';
}

最后根据返回的字符,打印相应的结果。 

3.游戏测试

        首先我们测试3×3的棋盘,编译并运行程序:

如果需要改变棋盘的大小,只需要改变符号ROW和COL的大小,例如改为4×4的棋盘:

#define ROW 4
#define COL 4

 

 4.代码链接

https://gitee.com/hkjz/for-c/tree/master/tic_tac_toe/tic_tac_toe

标签:int,三子,C语言,++,board,子棋,棋盘,col,row
From: https://blog.csdn.net/weixin_58888381/article/details/142261351

相关文章

  • C语言程序设计-练习篇
    千万不要放弃,最好的东西总是压轴出场。一,三角形的判断描述:已经给出三条边a,b,c,如果能构成三角形,判断三角形的类型(等边三角形,等腰三角形或普通三角形)。输入描述:题目有多组输入数据,每一行输入三个a,b,c(0<a,b,c<1000),作为三角形的三个边,用空格分隔。输出描述:针对每组输入数据,输......
  • 【时时三省】(C语言基础)指针进阶 例题7
    山不在高,有仙则名。水不在深,有龙则灵。              ----CSDN时时三省二维数组  第一个a因为它有12个元素每个元素占4个字节所以就打印48第二个a[0][0]表示是第一行第一个元素 所以它算的就是第一行第一个元素所占的大小所以就打印4第......
  • C语言:结构体
    在前面我们已经介绍了整形,浮点型,字符型,还介绍了数组,字符串。但是在实际问题中只有这些数据类型是不够的,有时候我们需要其中的几种一起来修饰某个变量,例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等,这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就......
  • c语言快递小项目
    structpack_head{  intfd;//通信的文件描述符  unsignedchartype; //消息类型  unsignedcharusertype;//用户类型:1:用户 2:快递员   charname[32]; //用户名  charpaaswd[32];//密码  charbuf[32]; //调试信息  stru......
  • C语言进阶【3】---C语言内存函数(你确定你很了解内存函数吗?)
    本章概述本章函数概述memcpy使用和模拟memmove使用和模拟memset函数的使用memcmp函数的使用彩蛋时刻!!!本章函数概述我们在本章的博客中讲的内容是有关内存的操作,我们直接通过内存块对数据进行操作。因为我们是直接对内存块操作,所以可以对任意类型数据进行操作(我们没......
  • C语言 13 指针
    指针可以说是整个C语言中最难以理解的部分了。什么是指针还记得在前面谈到的通过函数交换两个变量的值吗?#include<stdio.h>voidswap(int,int);intmain(){inta=10,b=20;swap(a,b);printf("a=%d,b=%d",a,b);}voidswap(inta,intb)......
  • 20240909_141725 c语言 整数类型
    整数型重点演练演练关于c99longlong类型是从c99版本开始有的C99是C语言的一个标准版本,全称为ISO/IEC9899:1999,是C语言的一个官方标准化版本,由国际标准化组织(ISO)和国际电工委员会(IEC)联合发布。C99标准在C89/ANSIC(1989年发布的C语言标准)的基础上进行了扩展和更新,引入了......
  • 20240909_151725 c语言 整数扩展
    完整形态类型后根int有无符号unsigned%u使用%u会约束输出无符号数据。如果是一个负数就会显示出错。使用%d可正常显示数据整数小结......
  • C语言程序设计(初识C语言后部分)
    不必为小事所困,我们还有更大的世界。(4)模拟实现strlen(求字符串长度)#include<stdio.h>#include<assert.h>//模拟实现strlen(求字符串长度)intmy_strlen(constchar*str)//str指向的数据不需要被修改{ intcount=0; assert(str); while(*str!='\0') { count++;......
  • C语言 ——— 学习并使用 #if …… #endif 条件编译指令
    目录何为条件编译指令常见的条件编译指令学习条件编译指令使用条件编译指令在程序预编译阶段,条件编译指令的代码转换多分支的条件编译指令何为条件编译指令在编译一个程序的时候,如果要将一条语句(一组语句)选择编译或者放弃时,就使用条件编译指令常见的条件编译指令#i......