三子棋(井字棋) 保姆级详解

               游戏整体思路:

1.编写前的准备

2.游戏的规则

3.游戏的菜单

4.井字棋棋盘的打印

5.玩家下棋与计算机下棋的实现

6.游戏的输赢判断

                编写前的准备:

编写前首先需要准备三个文件:

        1.负责测试游戏逻辑的  .c  文件。 -   test.c

        2.负责头文件包含以及函数声明的  .h  文件。 -   game.h

        3.负责实现游戏函数的  .c  文件。 -   game.c

游戏的规则

        双方依次在九宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。

由此可知,在开始游戏后首先会打印棋盘,其次玩家以输入坐标的方式进行下棋;下棋后电脑在一个空余的棋盘上进行下棋,当双方其中一方下完棋后为一条直线后,游戏判断为赢,否则为平局,游戏结束后重新选择选项是否继续游戏,否则退出。

游戏菜单的打印

        1.无论在进入什么游戏,首先应该有一个菜单,负责让玩家做选项,选择游戏时则开始游戏,选择退出时则退出游戏,选择错误则重新作选择,要实现该想法我们应该使用一个     do{ }  while( ); 循环语句;

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

}
int main()
{
	int chose = 0;
	do {
		menu();
		printf("Please enter your chose:>");
		scanf("%d",&chose);
		switch (chose) {
		case 1:
			printf("Tic-Tac-Toe\n");
			break;
		case 0:
			printf("Quit the game\n");
			break;
		default:
			printf("Input erro\n");
		}

	} 
	while (chose);

	return 0;
}

        在 test.c(负责测试游戏逻辑的.c文件)中创建一个 menu();函数,用来实现游戏菜单的显现,并在main()函数中的do{ } while()语句中调用,每当打开游戏或者是结束一局游戏后,都会调用该函数,打印一遍菜单;

将游戏菜单的打印实现后,应该实现的就是开始游戏,退出游戏的选择;

        从菜单可知,键盘输入1为开始游戏,0为退出游戏,既然是键盘输入,我们选择的则为getchar( ) \ scanf( ); 在使用getchar()函数时,可能会使下一次输入时读取到 ‘ \n ’ ,所以此处选择用scanf();,并将scanf()读取到的数据值赋给 int类型变量chose内 ;并用 开关语句(多分支语句)swithc( )   case:  语句,并将变量chose作为switch()语句的判断条件进行数据的判断:若键盘输入1,则开始三子棋游戏;若键盘输入0,则提示退出游戏并退出;若键盘输入其他数,则提示输入错误,并提示重新输入选项。

        同时将变量chose作为该处do{ } while( );语句的循环条件,若输入1,则恒为真,并进入游戏;输入0,则为假,跳出循环;输入其他时也恒为真,并提示重新输入。

 游戏棋盘的打印

        三子棋游戏中,玩家和电脑分别要在棋盘内进行下棋;所以在下棋之前,首先要实现游戏棋盘的打印和显现;三子棋的游戏是在一个九宫格内进行的,玩家和电脑需要在不同的坐标进行下棋,“九宫格” 相当于是3*3的小方格,从这里我们可以联想到 横为3 列为3 的二维数组。

       在switch语句中的 case 1 语句内创建一个game函数,每当玩家选择 1 时,则默认为游戏开始,进入game()函数;

void game()
{
//创建一个二维数组
	char board[ROW][COL] = {0};
	//棋盘的初始化
	Intboard(board, ROW, COL);
	//棋盘的打印
	Display(board, ROW, COL);

}

        在打印出棋盘前,应有相应的一个二维数组,为了加强代码的健壮性,可在负责头文件包含以及函数声明的  .h  文件 game.h 文件中创建一个 #define 定义的标识符常量 

                                                #define ROL 3        //行

                                                #define COL 3        //列

        确保当文件需要修改行列时可以一针见血。(应在 test.c 文件中#include "  "来包含此文件)

        

 

        三子棋棋盘的构造由空格、电脑与玩家两种不同的棋子以及每个格子的分界线组成,为了下步棋盘的美观及整齐性,应先将数组初始化为空格;在 game( ) 函数内调用一个Intboard()函数,用来初始化所创建的数组(在头文件中应先声明该函数,并在 game.c 文件中实现该函数),初始化空格有两种方法:

                1.使用 memset( ) 函数初始化数组的内存;

#include<string.h>//在使用memset()函数前应包含string.h头文件
void Intboard(char board[ROW][COL],int row ,int col)
{
    memset(&board[0][0], ' ',row*col*sizeof(board[0][0]));
}

                memset 函数为初始化函数,可以把一段连续的内存初始化某个值。

                2.使用迭代的方式将数组的各个元素遍历一遍,并将每个元素都初始化为 '     '

void Intboard(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] = ' ';
		}
	}
}

         此时二维数组内的元素都被初始化为了 ‘    ’ ,但并未出现棋盘该有的样子,再将棋盘的边框以打印的方式显现出来时,才能得到一个较为美观的棋盘。

        在 game( ) 函数内调用的 Intboard( ) 函数后再调用一个Display( )函数,(同样应先在 game.h 文件中先声明 ,并在game.c 文件中实现该函数)。

void Display(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]); //将每个' %c '看为一组
			if (j < col - 1) {
				printf("|");//每打印完一次' %c '打印一次 '|' ,最后一次则不打印
			}
		}
		printf("\n");//每打印完一行后打印换行
		if (i < row - 1) {//每打印一行后打印以下内容,最后一次则不打印
			int j = 0;
			for (j = 0; j < col;j++) {
				printf("---");//与上对应,将'---'看为一组进行打印
				if (j < col - 1) {
					printf("|");//每打印完一组'---',打印一个'|'作为分割线,最后一次不打印
				}
			}
		}
		printf("\n");//打印完一组后的换行
		}
	}

玩家下棋以及计算机下棋的实现

        玩家下棋

        玩家与计算机的下棋机制为回合制,在 game() 函数内玩家与计算机下棋应使用while循环,每下完一次棋判断一次输赢。

char ret = 0;
	while (1) {
		//玩家下棋;
		Playermove(board,ROW,COL);
		//玩家下棋后打印棋盘
		Display(board, ROW, COL);
	}
void Playermove(char board[ROW][COL], int row, int col) 
{
	int x = 0;
	int y = 0;
	while (1) {
		printf("The player continue:>\n");
		printf("Please enter your coordinate:>");
		scanf("%d%d", &x, &y);
		//判断输入坐标的合法性
		if (x > 0 && x <= row && y>0 && y <= col) {
			if (board[x - 1][y - 1] == ' ') {
				board[x - 1][y - 1] = '*';
				break;
			}
			else {
				printf("Coordinates are occupied.\n");
			}
		}
		else { 
			printf("The coordinate is erro;\n");
		}
	}
}

         在while()中调用一个Playermove()函数并在 game.c 文件中实现该函数),当轮到玩家下棋时,打印提示“玩家下棋”以及“请输入坐标",定义两个变量x 与 y,用scanf()从键盘中获取两个数并赋值给x,y的变量

        当x,y获取到两个数据时,应先判断坐标的合法性

        确保输入的坐标范围为 1-3 ,若是输入坐标不合法,则提示“坐标不合法,请重新输入坐标”。

        当坐标合法时则需要判断坐标是否被占用,若输入的坐标被占用时则提示“坐标被占用”。

        如果输入的标既合法且未被占用(字符内为空格),则将 ' * ' 赋值给该坐标;(输入的坐标范围为1-3,数组的下标为0-2,故下棋时的坐标行列都得-1)

        玩家下完棋后应再打印一次棋盘。

计算机下棋

        在while()中调用一个Coputermove()函数并在 game.c 文件中实现该函数),当轮到电脑下棋时,打印提示“计算机下棋”。计算机下棋时应使用随机数,在此之前应创建一个随机数生成器。

int main()
{
srand((unsigned int)time(NULL));
}

        在main()函数中使用 srand( ) 函数以时间戳来创建一个随机数生成器。应包含stdlib.h文件以及time.h文件),并在Computermove()函数中调用rand()函数来生成随机数(计算机生成的数可以以下标来设定,故%3,取出的范围则为0-2,确保坐标的合法性)

#include<time.h>
#include<stdlib.h>
void Computermove(char board[ROW][COL], int row, int col)
{
	printf("Computer move:>\n");
		int x = 0;
		int y = 0;
	while (1) {
		x = rand() % 3;
		y = rand() % 3;
		if (board[x][y] == ' ') {
			board[x][y] = '#';
			break;
		}
	}
}

        当该坐标为 ’   ‘ 时,则代表该处坐标未被占用,计算机则以此坐标落子。当计算机落完子后,应在 game( ) 函数内再打印一次棋盘,并判断输赢。

游戏输赢的判断

        游戏每进行一次则需要判断输赢一次

void game()
{
//创建一个二维数组
	char board[ROW][COL] = {0};
	//棋盘的初始化
	Intboard(board, ROW, COL);
	//棋盘的打印
	Display(board, ROW, COL);
	//玩家下棋以及计算机下棋的实现
	char ret = 0;
	while (1) {
		//玩家下棋;
		Playermove(board,ROW,COL);
		//玩家下棋后打印棋盘
		Display(board, ROW, COL);
		//判断输赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
		//计算机下棋
		Computermove(board,ROW,COL);
		Display(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
	}
	if (ret == '*') {
		printf("Players win!\n");
	}
	else if (ret == '#') {
		printf("Computers win!\n");
	}
	else 
		printf("The game draw.\n");
}

         创建一个char类型的变量ret用来接收 Iswin( ) 函数用来判断输赢的返回值。在此处调用一个名为 Iswin( ) 的char类型的函数

        当电脑赢时,则返回电脑的棋子 - #

        当玩家赢时,则返回玩家的棋子 - *

        当游戏平局时,返回 - 'Q'

        当游戏都不满足上述条件时,则返回 - ’C‘

char Iswin(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][0] != ' ') {
			return board[i][0];
		}
	}
	for (i = 0; i < col; i++) {
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {
			return board[0][i];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
		return board[0][0];
	}
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') {
		return board[2][0];
	}
	if (Isfull(board, row, col)) {
		return 'Q';
	}
	return 'C';
}

        赢的方式为三子连成一线,共有三行、三列、两对点八种结果

        除了判断是否赢还需判断游戏是否平局。

        在该函数内再调用一个int类型的自定义函数 Isfull( )

int Isfull(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++) {
			if (board[i][j] == ' ') {
				return 0;
			}
		}
	}
	return 1;
}

        当未出现任何赢家时则遍历一遍数组,若是数组中其中一个元素为空格,则返回0,if语句条件不成立,返回 ’C‘ ,若是遍历一遍数组内都没有元素为空格,则说明棋盘已满,且未出现赢家,则返回 ’Q‘;

        回到 game( ) 函数

                若是返回不为 ' C ' ,则跳出循环

                若是返回 ' * ',则玩家赢

                若是返回 ' # ',则电脑赢

                若是返回 ' Q ',则平局

 代码分享

                                                     test.c

#include<stdio.h>
#include<string.h>
#include"game.h"

void game()
{
//创建一个二维数组
	char board[ROW][COL] = {0};
	//棋盘的初始化
	Intboard(board, ROW, COL);
	//棋盘的打印
	Display(board, ROW, COL);
	//玩家下棋以及计算机下棋的实现
	char ret = 0;
	while (1) {
		//玩家下棋;
		Playermove(board,ROW,COL);
		//玩家下棋后打印棋盘
		Display(board, ROW, COL);
		//判断输赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
		//计算机下棋
		Computermove(board,ROW,COL);
		Display(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C') {
			break;
		}
	}
	if (ret == '*') {
		printf("Players win!\n");
	}
	else if (ret == '#') {
		printf("Computers win!\n");
	}
	else 
		printf("The game draw.\n");
}
void menu() 
{
	printf("******************************\n");
	printf("**********   1.play  *********\n");
	printf("******************************\n");
	printf("**********   0.exit  *********\n");
	printf("******************************\n");

}
int main()
{
	srand((unsigned int)time(NULL));
	int chose = 0;
	do {
		menu();
		printf("Please enter your chose:>");
		scanf("%d",&chose);
		switch (chose) {
		case 1:
			game();
			break;
		case 0:
			printf("Quit the game\n");
			break;
		default:
			printf("Input erro\n");
		}

	} 
	while (chose);

	return 0;
}

                                                     game.h

#define ROW 3
#define COL 3

//初始化棋盘
void Intboard(char board[ROW][COL], int row, int col);
//棋盘的打印,显示
void Display(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"
#include<stdio.h>


int Isfull(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++) {
			if (board[i][j] == ' ') {
				return 0;
			}
		}
	}
	return 1;
}

void Intboard(char board[ROW][COL], int row, int col)
{
	//memset(board, ' ', 9);//使用memset()初始化内存
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++) {
			board[i][j] = ' ';
		}
	}

}

void Display(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) {
			int j = 0;
			for (j = 0; j < col;j++) {
				printf("---");
				if (j < col - 1) {
					printf("|");
				}
			}
		}
		printf("\n");
		}

	}


void Playermove(char board[ROW][COL], int row, int col) 
{
	int x = 0;
	int y = 0;
	while (1) {
		printf("The player continue:>\n");
		printf("Please enter your coordinate:>");
		scanf("%d%d", &x, &y);
		//判断输入坐标的合法性
		if (x > 0 && x <= row && y>0 && y <= col) {
			if (board[x - 1][y - 1] == ' ') {
				board[x - 1][y - 1] = '*';
				break;
			}
			else {
				printf("Coordinates are occupied.\n");
			}
		}
		else { 
			printf("The coordinate is erro;\n");
		}
	}
}
#include<time.h>
#include<stdlib.h>
void Computermove(char board[ROW][COL], int row, int col)
{
	printf("Computer move:>\n");
		int x = 0;
		int y = 0;
	while (1) {
		x = rand() % 3;
		y = rand() % 3;
		if (board[x][y] == ' ') {
			board[x][y] = '#';
			break;
		}
	}
}


char Iswin(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][0] != ' ') {
			return board[i][0];
		}
	}
	for (i = 0; i < col; i++) {
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') {
			return board[0][i];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') {
		return board[0][0];
	}
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') {
		return board[2][0];
	}
	if (Isfull(board, row, col)) {
		return 'Q';
	}
	return 'C';
}

如有不足之处请兄弟萌批评指正!!!

敲码不易 记得三连!!!

 

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值