C实现三子棋

在这里插入图片描述

目录

  • 1. 分模块
  • 2. 游戏逻辑的实现
    • 2.1 初始化以及打印棋盘
    • 2.2 玩家/电脑下棋
    • 2.3 游戏实现代码


1. 分模块

实现三子棋,在写代码的时候要写很多行,所以需要把代码分模块管理。

  1. test.c //测试游戏逻辑
  2. game.c //游戏代码的实现
  3. game.h //游戏代码的声明(函数声明,符号定义)

把最基本的逻辑写一下,首先至少玩一把,上来就需要打印个菜单(1玩0退出),用do while循环,并且做相应的提示(菜单)让玩家选择,仔根据做出的选择做出响应,这时可以使用switch语句来实现。

2. 游戏逻辑的实现

void menu()
{
	puts("****************************");
	puts("******     1.开始     ******");
	puts("******     0.退出     ******");
	puts("****************************");
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入选项:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			puts("三子棋");
			break;
		case 0:
			puts("退出游戏");
			break;
		default:
			puts("输入错误!\n");
			break;
		}
	} while (input);
	return 0;
}

C实现三子棋_第1张图片

2.1 初始化以及打印棋盘

这里测试出没有bug的时候就要来实现三子棋了,现在case 1中封装game函数,来实现游戏的功能,三子棋需要一个3*3的棋盘也就是二维数组,玩家和电脑下棋分别需要不同的符号来标记并显示 ,因此是一个char类型的二维数组。

//此函数调用放在case 1中
void game()
{
	char board[3][3] = { 0 };
}

接下来就是下棋,需要打印一个3*3的棋盘。

然后玩家走,电脑走,同时还要判断输赢。

到这里就有了几个功能:初始化棋盘、打印棋盘、玩家走、电脑走和判断输赢。

最开始的棋盘应该是全部初始化为空格并打印,等玩家或电脑走的时候才开始显示字符。

void game()
{
	char board[3][3] = { 0 };
	//初始化棋盘函数
	Initboard(board, 3, 3);
}

把游戏功能函数声明放在头文件game.h中。
C实现三子棋_第2张图片
但是注意,如果参数全都是3,就把代码写死了,就是三子棋了,如果以后想改成五子棋十子棋就非常麻烦,涉及到3的地方全都要修改。

这时可以在头文件里用define来定义棋盘的行和列:
C实现三子棋_第3张图片
以后如果先修改棋盘,直接在这里修改就非常方便。

注:直接在头文件包含库函数,在两个源文件里包含头文件即可。
C实现三子棋_第4张图片

接着就是初始化棋盘函数的定义:

//全部初始化为空格
void Initboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

初始化之后观察里面的元素是否打印正确,需打印一下棋盘,每一次下棋都需要打印一次棋盘:

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘函数
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);
}

依然在game函数里调用,把声明和定义分别放在game.h头文件和game.c源文件中(参考初始化棋盘格式)。

打印函数的实现:

//打印棋盘
void Displayboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		//每打印一个元素就用|隔开
		//|的数量应该是元素-1
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		//作为每行元素的分割
		//打印次数也是元素-1次
		if (i < row - 1)
		{
			for (int x = 0; x < row; x++)
			{
				printf("---");
				if (x < row - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}

效果:
C实现三子棋_第5张图片

2.2 玩家/电脑下棋

接下来就是下棋了,只要玩家或者电脑出现横三竖三或者斜三相连的棋子就判定赢了。

在game里封装两个函数玩家和电脑移动,每次下棋打印出一次棋盘,反复的操作把这些步骤放在循环里。

void game()
{
	char board[ROW][COL] = { 0 };
	//初始化棋盘函数
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);

	while (1)
	{
		//玩家移动
		PlayerMove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//电脑移动
		ComputerMove(board, ROW, COL);
		Displayboard(board, ROW, COL);
	}
}

玩家移动函数实现:

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	puts("玩家下棋");

	while (1)
	{
		//虽然下标是从0开始
		//但是用户并不知道二维数组
		//要做出相应的调整
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
						    
		//坐标范围合法的判断
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//此时进行对应的坐标是否被占用
			//注意调整,应该把X和Y的值-1才对应的下标
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*'; 
				break;
			}
			else
			{
				puts("坐标被占用,请选择其他位置\n");
			}
		}
		else
		{
			puts("坐标非法,请重新输入\n");
		}	
	}
}

接着就是电脑下棋,跟玩家下棋相似,代码实现:

//电脑移动
//简单点,让其在空白的地方随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	puts("电脑下棋>");
	while (1)
	{
		//先让电脑随机生成下标
		//范围在0~2
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

玩家和电脑可以正常下棋后,每次下棋都需要判断输赢,在game函数里封装isWin函数:

char ret = 0;
char board[ROW][COL] = { 0 };
	puts("\n棋盘坐标有效范围(1~3)");
	//初始化棋盘函数
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);

while (1)
	{
		//玩家移动
		PlayerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//电脑移动
		ComputerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);
		Displayboard(board, ROW, COL);
	}

isWin函数该干什么?如果玩家或者电脑赢了,又或者平局了都要提示一下,这几种情况都不是说明游戏继续。
自己可以规定一下:玩家赢了返回 * ,电脑赢了返回 # ,平局返回 Q,继续返回 C,因此此函数的返回类型是char.

除了继续,只要是其他三种情况就退出游戏。

while (1)
	{
		//玩家移动
		PlayerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		Displayboard(board, ROW, COL);
		//电脑移动
		ComputerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		Displayboard(board, ROW, COL);
	}
	if (ret == '*')
	{
		puts("You win!");
	}
	else if (ret == '#')
	{
		puts("Computer win!");
	}						  
	else
	{
		puts("Draw");
	}

这是上面几种情况的反应,接下就是函数的实现:

//判断输赢
char isWin(char board[ROW][COL], int row, int col)
{
	//判断行
	for (int i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//判断列
	for (int j = 0; j < row; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
		{
			return board[0][j];
		}
	}
	//判断对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//到这里没人赢就要判断平局
	//判断数组是否还有空格
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 'C';
		}
	}

	//没有加就是平局了
	return 'Q';
}

这种判断输赢的方法就写死了,固定3*3,待我后面再想想动态的写法吧,来运行一下代码:
C实现三子棋_第6张图片
最后运行程序没报错,逻辑也没问题。简易的三子棋就算是完成了。

2.3 游戏实现代码

game.h头文件函数声明:

#pragma once
#include 
#include 
#include 

#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑移动
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char isWin(char board[ROW][COL], int row, int col);	

game.c函数定义:

#include "game.h"

//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

//打印棋盘
void Displayboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		//每打印一个元素就用|隔开
		//|的数量应该是元素-1
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		//作为每行元素的分割
		//打印次数也是元素-1次
		if (i < row - 1)
		{
			for (int x = 0; x < row; x++)
			{
				printf("---");
				if (x < row - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}

//玩家移动
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	puts("玩家下棋>");

	while (1)
	{
		//虽然下标是从0开始
		//但是用户并不知道二维数组
		//要做出相应的调整
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
						    
		//坐标范围合法的判断
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{													  
			//此时进行对应的坐标是否被占用
			//注意调整,应该把X和Y的值-1才对应的下标
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				puts("坐标被占用,请选择其他位置\n");
			}
		}
		else
		{
			puts("坐标非法,请重新输入\n");
			break;
		}
	}
}
	 
//电脑移动
//简单点,让其在空白的地方随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	puts("电脑下棋>");
	while (1)
	{
		//先让电脑随机生成下标
		//范围在0~2
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断输赢
char isWin(char board[ROW][COL], int row, int col)
{
	//判断行
	for (int i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//判断列
	for (int j = 0; j < row; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
		{
			return board[0][j];
		}
	}
	//判断对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//到这里没人赢就要判断平局
	//判断数组是否还有空格
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 'C';
		}
	}

	//没有就是平局了
	return 'Q';
}

test.c游戏主逻辑:

#include "game.h"

void menu()
{
	puts("****************************");
	puts("******     1.play     ******");
	puts("******     0.quit     ******");
	puts("****************************");
}

void game()
{
	char ret = 0;
	char board[ROW][COL] = { 0 };
	puts("\n棋盘坐标有效范围(1~3)");
	//初始化棋盘函数
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);

	while (1)
	{
		//玩家移动
		PlayerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);		
		Displayboard(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑移动
		ComputerMove(board, ROW, COL);
		//判断输赢
		ret = isWin(board, ROW, COL);
		Displayboard(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		puts("You win!\n");
	}
	else if (ret == '#')
	{
		puts("Computer win!\n");
	}						  
	else
	{
		puts("Draw!\n");
	}
}
int main()
{
	int input = 0;
	//设置随机数的起点生成
	srand((unsigned)time(NULL));

	char arr[ROW][COL] = { 0 };
	do
	{
		menu();
		printf("请输入选项:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;				
		case 0:
			puts("退出游戏\n");
			break;
		default:
			puts("输入错误!\n");
			break;
		}
	} while (input);
	return 0;
}

以上就是三子棋实现的代码,还有好多可以优化的地方,待我慢慢改善。


你可能感兴趣的:(c语言,c语言,开发语言,c++)