• 【项目】三子棋小游戏(C语言)


    【项目】—— 三子棋(C语言)

    一、游戏介绍

    三子棋就是三颗棋子连成一线就赢的棋,非常简单,我们使用C语言来实现一个人机对战的三子棋。虽然实现的是三子棋,但是咱们的三子棋具有多子棋的逻辑,只要修改头文件的常量就能实现任意子棋。

    三子棋的功能

    1. 游戏开始界面有一个 R × C R\times C R×C的棋盘
    2. 玩家可以输入坐标落子
    3. 电脑在玩家落子后自动落子
    4. 每次落子后判断输赢,三颗棋子连成一线则赢

    二、设计思路与文件构成

    1. 设计思路

    1. 创建一个 r o w × c o l row \times col row×col的二维数组
    2. 玩家输入落子坐标,在该坐标位置赋为字符*
    3. 电脑随机在未落子的位置进行落子,在该位置赋值为字符#
    4. 每次落子判断输赢,遍历数组,若是有三子一线的情况则对局结束

    2. 文件构成

    1. game.h:头文件,用于引入头文件,声明常量和声明函数
    2. game.c:源文件,用于实现游戏的功能
    3. test.c:测试文件,用于测试游戏代码的逻辑,并写有main函数启动程序

    三、头文件

    修改该文件中的ROWCOL常量就可以修改棋盘的大小,修改WIN常量可改变胜利的条件,完成WIN子棋的改变

    #pragma once
    #include 
    #include 
    #include 
    #include 
    
    #define ROW 5	//棋盘行数
    #define COL 5	//棋盘宽度
    #define WIN 3	//胜利条件
    
    
    void menu();													//菜单
    void Init(char board[ROW][COL], int row, int col);					//初始化棋盘
    void DisplayBoard(char board[ROW][COL], int row, int col);			//打印棋盘
    void PlayMove(char board[ROW][COL], int row, int col);				//玩家落子
    void ComputerMoveAndPrint(char board[ROW][COL], int row, int col);	//电脑落子
    char IsWin(char board[ROW][COL], int row, int col);					//判断输赢
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四、代码实现

    1. 初始化

    将棋盘的所有字符初始化为空格

    void Init(char board[ROW][COL], const int row, const int col)
    {
    	int i = 0;
    	for (i = 0; i < row; i++)
    	{
    		int j = 0;
    		for (j = 0; j < col; j++)
    		{
    			board[i][j] = ' ';
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. 打印棋盘

    打印一个 r o w × c o l row\times col row×col的棋盘,并输出棋盘中的字符

    void DisplayBoard(char board[ROW][COL], const int row, const int col)
    {
    	//打印列坐标
    	printf("   ");
    	for (int j = 0; j < col; j++)
    	{
    		printf(" %d  ", j + 1);
    	}
    	printf("\n");
    
    	int i = 0;
    	for (i = 0; i < row; i++)
    	{
    		printf("%d- ", i + 1);		//打印行坐标
    		int j = 0;
    		for (j = 0; j < col; j++)
    		{
    			if (j < col - 1)
    			{
    				printf(" %c |", board[i][j]);
    			}
    			else
    			{
    				printf(" %c \n", board[i][j]);
    			}
    		}
    
    		if (i < row - 1)
    		{
    			printf("   ");
    			for (j = 0; j < col; j++)
    			{
    				if (j < col - 1)
    				{
    					printf("---|");
    				}
    				else
    				{
    					printf("---\n");
    				}
    			}	
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    3. 玩家落子

    玩家输入坐标,判断坐标的正确性和是否被占用,若是没有将该位置赋值为*

    void PlayMove(char board[ROW][COL], int row, int col)
    {
    	printf("玩家下棋:>\n");
    	int x = 0, y = 0;
    	while (1)
    	{
    		printf("请输入坐标(用空格隔开):>");
    		scanf("%d %d", &x, &y);
    		if ((x >= 1) && (x <= row) && (y >= 1) && (y <= col))
    		{
    			x--;
    			y--;
    			if (board[x][y] != ' ')
    			{
    				printf("坐标被占用\n");
    			}
    			else
    			{
    				board[x][y] = '*';
    				break;
    			}
    		}
    		else
    		{
    			printf("坐标输入有误,请重新输入");
    		}
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4. 电脑落子

    电脑随机生成一个坐标,将该位置赋值为#,并打印棋盘,并打印电脑下棋坐标

    void ComputerMoveAndPrint(char board[ROW][COL], int row, int col)
    {
    	int x = 0, y = 0;
    	while (1)
    	{
    		x = rand() % row;
    		y = rand() % col;
    
    		if (' ' == board[x][y])
    		{
    			board[x][y] = '#';
    			DisplayBoard(board, ROW, COL);
    			printf("电脑下棋>%d %d\n", x + 1, y + 1);
    			break;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5. 判断输赢

    1. 三子棋的胜利条件是某一行、某一列或者对角线上实现三子一线。若是将三子棋写死为固定三子,则可以使用穷举法将所有条件判断完。但是作为一个有理想有抱负没水平的学者,我们的三子棋要具备实现多子棋的拓展性。
    2. 判断输赢的方式分开为判断行赢、列赢和对角线赢。
    3. 若是棋盘满了还没有连成一线,则平局。
    4. 我们使用遍历二维数组,计数连续的相同棋子的方式实现。

    5.1 判断行赢

    ​ 遍历每一行,使用conut记录每一行指定棋子c的个数,边记录边判断连续的个数是否达到胜利条件WIN的个数,若是中途遇到其他棋子则将count置为0

    _Bool XofV(char board[ROW][COL], int row, int col, char c)
    {
    	int i = 0;
    	for (i = 0; i < row; i++)
    	{
    		int count = 0;
    		int j = 0;
    		for (j = 0; j < col; j++)
    		{
    			if (c == board[i][j])
    			{
    				count++;
    				if (count >= WIN)	//判断是否赢
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.2 判断列赢

    根判断行赢的逻辑一样,所以我们直接将行列交换完成实现

    _Bool YofV(char board[ROW][COL], int row, int col, char c)
    {
    	int i = 0;
    	for (i = 0; i < col; i++)
    	{
    		int count = 0;
    		int j = 0;
    		for (j = 0; j < row; j++)
    		{
    			if (c == board[j][i])
    			{
    				count++;
    				if (count >= WIN)
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.3 判断对角线赢

    ​ 正对角和斜对角都要遍历,每个遍历分为2个步骤,分别是上三角和下三角

    _Bool XandYofV(char board[ROW][COL], int row, int col, char c)
    {
    	//正对角
    	int i = 0;
    	for (i = 0; i < row; i++)
    	{
    		int count = 0;
    		int j = 0;
    		int x = i;
    		for (j = 0; j < col && x < row; j++, x++)
    		{
    			if (board[x][j] == c)
    			{
    				count++;
    				if (count >= WIN)
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    			
    		}
    	}
    
    	int j = 0;
    	for (j = 1; j < col; j++)	//上三角已经把对角线遍历过了,所以下三角不用遍历0对角线
    	{
    		int count = 0;
    		int i = 0;
    		int y = j;
    		for (i = 0; i < row && y < col; i++, y++)
    		{
    			if (board[i][y] == c)
    			{
    				count++;
    				if (count >= WIN)
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    		}
    	}
    
    	//斜对角
    	for (j = col-1; j >= 0; j--)
    	{
    		int count = 0;
    		int y = j;
    		int i = 0;
    		for (i = 0; y >= 0 && i < row; y--, i++)
    		{
    			if (board[i][y] == c)
    			{
    				count++;
    				if (count >= WIN)
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    
    		}
    	}
    
    	for (i = 1; i < row; i++)
    	{
    		int count = 0;
    		int x = i;
    		int j = col - 1;
    		for (j = col - 1; j >= 0 && x < row; x++, j--)
    		{
    			if (board[x][j] == c)
    			{
    				count++;
    				if (count >= WIN)
    				{
    					return 1;
    				}
    			}
    			else
    			{
    				count = 0;
    			}
    
    		}
    	}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    5.4 判断棋盘满

    ​ 遍历棋盘,若是没有空格则棋盘满了

    _Bool IsFull(char board[ROW][COL], int row, int col)
    {
    	int i = 0;
    	for (i = 0; i < row; i++)
    	{
    		int j = 0;
    		for (j = 0; j < col; j++)
    		{
    			if (' ' == board[i][j])
    			{
    				return 0;
    				break;
    			}
    		}
    	}
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.5 判断棋子输赢

    将玩家或电脑棋子表示的字符作为参数,比较行、列、对角线,若是都没有赢的,判断棋盘满了,则为平局

    1. 玩家赢返回*
    2. 电脑赢返回#
    3. 棋盘满了为平局,返回q
    4. 若是没有人赢且棋盘没满则继续,返回c
    char IsWin(char board[ROW][COL], int row, int col)
    {
        //玩家赢
    	if (XofV(board, row, col, '*') || YofV(board, row, col, '*') || XandYofV(board, row, col, '*'))
    	{
    		return '*';
    	}
        //电脑赢
    	else if (XofV(board, row, col, '#') || YofV(board, row, col, '#') || XandYofV(board, row, col, '#'))
    	{
    		return '#';
    	}
        //棋盘满
    	else if(IsFull(board, row, col))
    	{
    		return 'q';
    	}
        //对局继续
    	else
    	{
    		return 'c';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    五、测试文件

    1. 菜单

    让用户选择的界面

    void menu()
    {
    	printf("******************************\n");
    	printf("********    1. play    *******\n");
    	printf("********    0. exit    *******\n");
    	printf("******************************\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 游戏实现

    void game()
    {
    	//创建棋盘
    	char board[ROW][COL] = { 0 };
    	char win = ' ';
    
    	Init(board, ROW, COL);
    	DisplayBoard(board, ROW, COL);
    
    	//双方对局
    	while (1)
    	{
    		PlayMove(board, ROW, COL);
    		DisplayBoard(board, ROW, COL);
    		win = IsWin(board, ROW, COL);
    		if (win != 'c')
    		{
    			break;
    		}
    		
    		system("cls");
    		ComputerMoveAndPrint(board, ROW, COL);
    		win = IsWin(board, ROW, COL);
    		if (win != 'c')
    		{
    			break;
    		}
    	}
    	
    	//判定结果
    	system("cls");
    	DisplayBoard(board, ROW, COL);
    	if ('*' == win)
    	{
    		printf("玩家胜利\n");
    	}
    	else if ('#' == win)
    	{
    		printf("电脑胜利\n");
    	}
    	else
    	{
    		printf("平局\n");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    3. main函数

    启动游戏,并调用菜单供用户选择参考

    int main(void)
    {
    	int input = 0;
    	srand(time(NULL));
    	do
    	{
    		menu();
    		printf("请操作>");
    		scanf("%d", &input);
    
    		switch (input)
    		{
    		case 1:
    			system("cls");
    			game();
    			break;
    
    		case 0:
    			printf("欢迎下次光临~\n");
    			break;
    
    		default:
    			printf("数字输入错误\n");
    			break;
    		}
    
    	} while (0 != input);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    六、完整代码

    代码存储于gitee中:点击查看完整代码

  • 相关阅读:
    C++ Reference: Standard C++ Library reference: C Library: cctype: isxdigit
    echarts双轴刻度线y轴刻度线对齐
    优化算法 - Adadelta
    基于ASAM ODS标准的试验数字化平台-WDP
    《大数据之路:阿里巴巴大数据实践》-第1篇 数据技术篇 -第5章 实时技术
    【Java】已解决java.nio.channels.ClosedChannelException异常
    浅析显卡市场的未来走向:现在可以抄底了吗?
    U++学习笔记 ------ 多播委托
    Spark.示例
    Java | abstract关键字【面向对象的第三大特征——多态】
  • 原文地址:https://blog.csdn.net/weixin_52811588/article/details/126695277