【3】 三子棋

        文法不好,尝试用图文来代替文字写一次博客,写的不好,有点乱,思路可以参考,代码会放在页尾,主要给自己看的,对学到知识的一个总结,渣渣一个,不喜勿喷T_T

        第一次写三子棋的时候,还是很慌的,毕竟以前没有写过这么大的工程,但其实把他想明白了,不难的,你会发现,他就是个缝合怪,把他拆分成若干个函数,你会发现都是以前学过的东西。

        第一次写的时候bug很多,还找不出来,也懒得去调试,总会留下没有解决的隐患,写完之后,以前没解决的问题,变成连锁反应暴露出来了,第二次写的时候,把要实现的功能罗列出来,思路清晰了,做一小块就调试一下,保证每个模块都能正常运行,就算以后写代码出bug了,也不用担心是前面代码的问题,因为前面是调试过,保证没问题的

0. 三子棋介绍

1.游戏入口

        1.菜单:menu

//打印:菜单
void menu()
{
	printf("********************\n");
	printf("****   1.play   ****\n");
	printf("****   0.exit   ****\n");
	printf("********************\n\n");
}

        2.选择:玩不玩?

	int input;
	do
	{
		menu();                    //打印菜单
		printf("请选择(1/0):");
		scanf("%d", &input);

		//清缓冲区
		//while (getchar() != '\n')
		//{
		//}

		switch (input)
		{
		case 1:
			printf("三子棋\n\n");
			game();					//进入三子棋游戏
			break;
		case 0:
			printf("退出游戏\n\n");
			break;
		default:
			printf("选择错误,请重新选择\n\n");
			break;
		}
	} while (input);

2.游戏的实现

          1.棋子:二维数组

         2.初始化数组

 

 

//初始化数组
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;

	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			//初始化为空格
			board[i][j] = ' ';
		}
	}
}



题外话!!

        1.关于函数

我们要养成:把行数多,且功能独立的代码,封装成函数,的良好习惯

比如:这是三子棋的运行流程,有序的一个接一个的运行,阅读起来简洁易懂,没有障碍

 不敢想象,把所有东西都塞进主程序是什么样的效果,杂乱无章,想改都无从改起,维护成本极高。。

         2.头文件/源文件

若要调用game.c的内容,则需要
        1.game.h的返回类型 / 函数名 / 形参;与定义时一致
        2.test.c   需引头文件  “game.h”

在:

//在主程序,test.c中
#include"game.h"
{
    InitBoard(board, ROW, COL);
}


//在函数程序,game.c中
void InitBoard(char board[ROW][COL], int row, int col)
{
    //函数效果;
}


//头文件,game.h中
void InitBoard(char board[ROW][COL], int row, int col);
// 返回类型 / 函数名 / 形参 / 必须与game.c一致

        3.宏定义

 格式如下:

//在game.h中
#define ROW 3
#define COL 3

  棋盘

 那么。。。在不能用手指的情况下,要怎么弄呢。。
我们先看看棋盘长什么样

   |   |   
---|---|---
   |   |   
---|---|---
   |   |   

不会??这其实就是刷题中的,操控循环和if语句,控制输出的内容,最终输出某种特定图形

图为牛客网中的,各种牛鬼蛇神

 那么在有大概的认识之后,就可以分析一波棋盘的构成,以及如何用学过的知识来输出了

 代码如下:

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i, j;

	for (i = 0; i < row; i++)
	{

		//数据行
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)				//最后1列,不输出 |
				printf("|");
		}
		printf("\n");


		//分割行
		if (i < row - 1)					//最后1行,不输出 ---|---|---
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)			//最后1列,不输出 |
					printf("|");
			}
			printf("\n");
		}
	}
	printf("\n");

}

 那么棋盘/ 棋子/的问题都已经解决了
 接下来我们来实现下棋的代码

下棋

 玩家下棋回合:代码如下:

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x, y;

	while (1)
	{
		printf("玩家走:");
		scanf("%d%d", &x, &y);

		//清缓冲区
		while (getchar() != '\n')
		{
		}

		//坐标是否越界?
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//坐标是否有棋子
			if (board[x - 1][y - 1] == ' ')
			{
				//下棋'*'
				board[x - 1][y - 1] = '*';
				break;
			}

			else
				printf("坐标已有棋子,");
		}
		else
			printf("坐标不在棋盘内,:");
	}
}

 电脑下棋:代码如下:

//与玩家的下棋一样,随机数代替输入,且不用考虑玩家角度的坐标,以及越界的问题
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x, y;

	while (1)
	{
		x = rand() % row; //余0  1  2
		y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
	printf("电脑走:%d %d\n", x + 1, y + 1);

}

判断输赢

 

 

代码如下:

    //行	 00 01 02		10 11 12	
	for (i = 0; i < row; i++)
	{
		cnt = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
				cnt++;

			if (cnt == col - 1)
			{
				ch = board[i][j];
				goto win;
			}
		}



		//列	 00 10 20		01 11 21	
		for (j = 0; j < col; j++)
		{
			cnt = 0;
			for (i = 0; i < col - 1; i++)
			{
				if (board[i][j] == board[i + 1][j] && board[i][j] != ' ')
					cnt++;

				if (cnt == col - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}



		//对角线1	 00 11 22
		//创建a,b用于替代循环变量i,j

		int a, b;
		for (i = 0; i < row; i++)
		{
			for (j = 0; j < col; j++)
			{
				//防止a+1  b+1   越界
				cnt = 0;
				for (a = i, b = j; a < row - 1 && b < col - 1; a++, b++)
					if (board[a][b] == board[a + 1][b + 1] && board[a][b] != ' ')
						cnt++;

				if (cnt == col - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}





		//对角线2	 02 11 20

		for (i = 0; i < row; i++)
		{
			for (j = 0; j < col; j++)
			{
				//防止a+1  b-1   越界
				cnt = 0;
				for (a = i, b = j; a < row - 1 && b >= 1; a++, b--)
					if (board[a][b] == board[a + 1][b - 1] && board[a][b] != ' ')
						cnt++;

				if (cnt == col - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}

代码如下:

//不可以在IsWin函数内定义函数哟(嵌套定义)
int IsFull(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
			//棋盘有空的,返回0
			if (board[i][j] == ' ')
				return 0;
	}

	//满了,返回1
	return 1;
}



//调用函数IsFull()
int full = IsFull(board, ROW, COL);

    //下列条件,满足其一,返回0  (中止游戏)
	win:
		if (ch == '*')
		{
			printf("玩家赢!\n\n");
			return 0;
		}
		else if (ch == '#')
		{
			printf("电脑赢!\n\n");
			return 0;
		}
		else if (full == 1)
		{
			printf("平局\n\n");
			return 0;
		}


		//否则返回1 (继续游戏)
		return 1;

	}

执行流程:
1.先初始化棋盘,下棋之前打印1次棋盘
2.进入循环,玩家和电脑轮流下,每下完一步棋,输出1次棋盘,可以观察下棋的实时状况
3.若电脑或玩家的其中一方,有3连子了,或棋盘满了,都会中止游戏,已经没有玩下去的必要了

测试

我们还可以将游戏改成5子棋,棋盘大小也可以调节,因为棋盘大小,几行几列,都是用宏来当作参数传给函数的,n连子也是用利用循环的,没有把代码写死,那么我们只需要修改一下宏的定义,即可修改成更大的棋盘,和更难的胜利条件

 各个方位都能胜利,目前没有发现BUG
但由于棋盘大小的增大,电脑下棋的坐标是随机的,堵到的概率很低,所以很无聊
可以写的更智能一点,如5子棋,玩家在棋盘上超过2个或3个子就会开堵
或者说,电脑可以检测在棋盘上自己有没有多个相连的棋子,如果有,就不堵玩家,优先下够5个相连的棋子

说得简单,做起来很烧脑,而且代码量会翻一倍吧

代码:

主程序  test.c

//主程序 test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"


//三子棋
//   |   |   
//---|---|---
//   |   |   
//---|---|---
//   |   |   



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

void game()
{
	char board[ROW][COL] = { 0 };					//游戏需输入坐标,坐标为x,y,即2维数组
	int win = 0;									//win 接收IsWin的返回值,通过返回值决定是否继续


	InitBoard(board, ROW, COL);	
	printf("%d子棋,棋盘大小:%d*%d\n",WinCnt, ROW, COL);	

	DisplayBoard(board, ROW, COL);	


	while (1)
	{

		PlayerMove(board, ROW, COL);				//玩家走
		DisplayBoard(board, ROW, COL);				//走一步看一步

		win = IsWin(board, ROW, COL);				//判断输赢
		if (win == 0)
			break;



		ComputerMove(board, ROW, COL);				//电脑走
		DisplayBoard(board, ROW, COL);				//走一步看一步

		win = IsWin(board, ROW, COL);				//判断输赢
		if (win == 0)
			break;
	}
}

void test()
{
	int input;
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		printf("请选择(1/0):");
		scanf("%d", &input);

		//清缓冲区
		while (getchar() != '\n')
		{
		}

		switch (input)
		{
		case 1:
			printf("%d子棋\n\n",WinCnt);
			game();					//进入游戏
			break;
		case 0:
			printf("退出游戏\n\n");
			break;
		default:
			printf("选择错误,请重新选择\n\n");
			break;
		}
	} while (input);

}


int main()
{
	test();
	return 0;
}

game.c  游戏函数程序

//game.c  游戏函数程序
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"


void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;

	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			//初始化为空格
			board[i][j] = ' ';
		}
	}
}





void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i, j;

	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");
		}
	}
	printf("\n");

}





void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x, y;

	while (1)
	{

		printf("玩家走:");
		scanf("%d%d", &x, &y);

		//清缓冲区
		while (getchar() != '\n')
		{
		}

		//坐标是否越界?
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//坐标是否有棋子
			if (board[x - 1][y - 1] == ' ')
			{
				//下棋'*'
				board[x - 1][y - 1] = '*';
				break;
			}

			else
				printf("坐标已有棋子,");
		}
		else
			printf("坐标不在棋盘内,:");
	}
}




//与玩家的下棋一样,随机数代替输入,且不用考虑玩家角度的坐标,以及越界的问题
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x, y;

	while (1)
	{
		x = rand() % row; //余0  1  2
		y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
	printf("电脑走:%d %d\n", x + 1, y + 1);

}





//判断棋盘是否已满
int IsFull(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
			//棋盘有空的,返回0
			if (board[i][j] == ' ')
				return 0;
	}

	//满了,返回1
	return 1;
}








int IsWin(char board[ROW][COL], int row, int col)
{
	int i, j;
	int cnt = 0;	//连续棋子统计
	char ch = 0;



	//行	 00 01 02		10 11 12	
	for (i = 0; i < row; i++)
	{
		cnt = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] == board[i][j + 1] && board[i][j] != ' ')
				cnt++;

			if (cnt == WinCnt -1)
			{
				ch = board[i][j];
				goto win;
			}
		}



		//列	 00 10 20		01 11 21	
		for (j = 0; j < col; j++)
		{
			cnt = 0;
			for (i = 0; i < col - 1; i++)
			{
				if (board[i][j] == board[i + 1][j] && board[i][j] != ' ')
					cnt++;

				if (cnt == WinCnt - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}



		//对角线1	 00 11 22
		//创建a,b用于替代循环变量i,j

		int a, b;
		for (i = 0; i < row; i++)
		{
			for (j = 0; j < col; j++)
			{
				//防止a+1  b+1   越界
				cnt = 0;
				for (a = i, b = j; a < row - 1 && b < col - 1; a++, b++)
					if (board[a][b] == board[a + 1][b + 1] && board[a][b] != ' ')
						cnt++;

				if (cnt == WinCnt - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}





		//对角线2	 02 11 20

		for (i = 0; i < row; i++)
		{
			for (j = 0; j < col; j++)
			{
				//防止a+1  b-1   越界
				cnt = 0;
				for (a = i, b = j; a < row - 1 && b >= 1; a++, b--)
					if (board[a][b] == board[a + 1][b - 1] && board[a][b] != ' ')
						cnt++;

				if (cnt == WinCnt - 1)
				{
					ch = board[i][j];
					goto win;
				}
			}
		}





		int full = IsFull(board, ROW, COL);

		//下列条件,满足其一,返回0  (中止游戏)
	win:
		if (ch == '*')
		{
			printf("玩家赢!\n\n");
			return 0;
		}
		else if (ch == '#')
		{
			printf("电脑赢!\n\n");
			return 0;
		}
		else if (full == 1)
		{
			printf("平局\n\n");
			return 0;
		}


		//否则返回1 (继续游戏)
		return 1;

	}
}

 game.h   声明头文件 

//game.h 声明头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9		//棋盘( N )行
#define COL 9		//棋盘( N )列
#define WinCnt 5	//( N )子棋


//初始化棋盘
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);


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


//IsWin的子函数,用于检查棋盘是否满了
int IsFull(char board[ROW][COL], int row, int col);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值