目录
问题描述
逻辑分析
具体实现
1.进入菜单界面
2.初始化棋盘
3.打印棋盘
4.玩家下棋
5.电脑下棋
6.判断输赢
运行结果
完整代码
game.h
game.c
test.c
问题描述
结合C语言所学知识,简单实现一个三子棋小游戏。
逻辑分析
- 进入菜单界面
 - 初始化棋盘
 - 打印棋盘
 - 玩家下棋
 - 判断输赢
 - 电脑下棋
 - 判断输赢
 - 游戏结束
 
具体实现
1.进入菜单界面
玩家通过输入1或者0来选择进入对应的环节:输入“1”则进入游戏环节;输入“0”则退出游戏;如果输入其他数字,则会提示“选择错误”
void menu()
{
	printf("****************************\n");
	printf("*******    1.play    *******\n");
	printf("*******    0.exit    *******\n");
	printf("****************************\n");
}
void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			//printf("三子棋\n");
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
} 
运行结果:

2.初始化棋盘
棋盘格式设置为3*3的二维数组大小,元素类型为char,初始化为空格,即什么数据也不存储。行列采用宏定义的方式,是为了方便后期对棋盘的大小进行统一修改,同时也为了提高代码的可读性
#define ROW 3
#define COL 3
//初始化棋盘为全空格
void InitBoard(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++)
		{
			board[i][j] = ' ';
		}
	}
} 
3.打印棋盘
将棋盘进行拆分,第一行为: 字符 | 字符 | 字符 (注意字符前后的空格),第二行我们为:---|---|---。在打印数据时,根据for循环,打印三行数据,也就是三行字符 | 字符 | 字符 ,同时三行数据中穿插着两行---|---|---作为分隔。
//打印棋盘
void DisplayBoard(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++)
		{
			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");//换行
		}
	}
} 
运行结果:

4.玩家下棋
玩家根据提示输入行列坐标,比如1 1,同时使用’*’表示玩家落子。玩家在落子之前需要对坐标进行合法性的判断,即判断所输入的坐标是否在棋盘规定的范围内以及是否已被占用。玩家要选择在棋盘上空闲的位置进行落子,否则会提示出错。输入坐标合法之后要立即跳出循环,否则会陷入死循环。
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	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;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
} 
运行结果:

5.电脑下棋
在玩家落子之后,电脑会随机生成行列坐标并在棋盘上空闲的地方进行相应的落子,'#’表示电脑落子。生成随机数之前需要基于当前时间设定“种子”的值srand((unsigned int)time(0))。在调用rand()函数之前,要先在主函数中调用srand函数。如果没有上述准备工作,那么种子的值就会默认为1,会生成相同的随机数序列。
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
} 
运行结果:

6.判断输赢
在判断输赢时,我们约定:‘*’ 表示玩家赢;‘#’ 表示电脑赢;’ Q’ 表示平局;‘C’ 表示继续
玩家赢:行,列以及两条对角线有三个连续的‘*’则表示玩家赢
电脑赢:行,列以及两条对角线有三个连续的‘#’则表示电脑赢
平局:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘已满,则表示平局
继续:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘未满,则游戏继续
//判断平局
static int is_full(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;//没满
			}
		}
	}
	return 1;//满了
}
//判断输赢
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	
	//判断行
	for (i = 0; i < row; i++)
	{
		if (board[i][0]==board[i][1]&&board[i][1]==board[i][2] &&board[i][1]!= ' ')
		{
			return board[i][1];
		}
	}
	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}
	//判断对角线
	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];
	}
	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	return 'C';
} 
运行结果
玩家赢:

电脑赢:

平局:

完整代码
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.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 player_move(char board[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);
//判断输赢
char is_win(char board[ROW][COL], int row, int col); 
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘为全空格
void InitBoard(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++)
		{
			board[i][j] = ' ';
		}
	}
}
//打印棋盘
//版本1.0
//void DisplayBoard(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++)
//		{
//			printf("%c",board[i][j]);//打印的是棋盘初始化中的空格
//		}
//		printf("\n");
//	}
//}
//版本2.0
//存在缺陷,行数可以改变,但是列数被固定死无法修改
//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");
//		}
//	}
//}
//版本3.0
void DisplayBoard(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++)
		{
			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");//换行
		}
	}
}
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");
	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;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
}
//判断平局
static int is_full(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;//没满
			}
		}
	}
	return 1;//满了
}
//判断输赢
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	
	//判断行
	for (i = 0; i < row; i++)
	{
		if (board[i][0]==board[i][1]&&board[i][1]==board[i][2] &&board[i][1]!= ' ')
		{
			return board[i][1];
		}
	}
	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}
	//判断对角线
	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];
	}
	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	return 'C';
} 
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
//test.c:测试三子棋游戏的逻辑
//game.c:游戏的实现
//game.h:函数声明,符号的定义
void menu()
{
	printf("****************************\n");
	printf("*******    1.play    *******\n");
	printf("*******    0.exit    *******\n");
	printf("****************************\n");
}
void game()
{
	char ret = 0;
	//存放下棋的数据
	char board[ROW][COL] = { 0 };
	//初始化棋盘为全空格
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret=is_win(board,ROW,COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		computer_move(board, ROW, COL);//随机下棋,哪里空着,往哪里下
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢了\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢了\n");
	}
	else
	{
		printf("平局\n");
	}
	//打印棋盘
	DisplayBoard(board, ROW, COL);
}
//什么情况,游戏就结束了呢?
//玩家赢:'*'
//电脑赢:'#'
//平局:'Q'
//继续:'C'
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			//printf("三子棋\n");
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
} 
                


















