三子棋的实现–二维数组的应用

通过对数组,函数,循环知识的应用我们可以独立地创建一个项目--三子棋。首先我们对于三子棋的实现要有一个大概的思路和逻辑。

文件的创建

工欲善其事必先利其器,为了更好地完成项目,先创建三个文件--两个源文件,一个头文件。测试文件-test.c,游戏文件game.c,游戏头文件game.h。游戏文件主要就是存放与游戏过程实现有关代码,例如:初始化函数,打印棋盘函数等;测试文件主要就是存放main函数和一些辅助实现的函数;游戏头文件里就是游戏文件中函数的声明,做头文件被测试文件引入。

先在test.c中把主函数写一写:

int main()
{
	test();
	return 0;
}
//将测试单独写成一个函数,分块处理,使逻辑更加清晰

再把test函数写一写,当程序运行到test函数时,就已经进入到游戏的初始界面啦。

#include<stdio.h>


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:
            game();
            break;
        case 0:                              //因为游戏玩一次常常玩不够,所以选择的是循环结构
            printf("离开游戏n");
            break;
        default:
            printf("输入错误请重新输入");
            break;
        }
    }while(input); 
}


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

大体思路

1.下的棋子也算是数据,数据要存储在哪里?

2.要先初始化棋盘,并且打印出来,看看初始的棋盘是怎样的。

3.游戏开始,玩家下一步,电脑下一步。要清楚这是一个有条件的循环过程。

4.判断输赢,判断输赢的根据又是什么?

在game()的函数中写下思路

void game()
{
    char board[3][3] = {0};//初始化一个数组用来存放数据
    //初始化棋盘;
    //打印棋盘;
    //游戏开始:玩家走,电脑走。循环的过程。
    //判断输赢。
}

为了使程序更具改造性,在game.h中宏定义。之后就用字符串来表示数字,以后需要改成五子棋,十字棋等等,只需要在头文件这里修改3为5,10就好了。

#define ROW 3
#define COL 3
void game()
{
    char board[3][3] = {0};//初始化一个数组用来存放数据
    //初始化棋盘;
    //打印棋盘;
    //游戏开始:玩家走,电脑走。循环的过程。
    //判断输赢。
}

1.数据的储存

三子棋--三行三列。这样的数据一定是被存储在一组二维数组中的。并且是字符类型的数组中。

2.初始化棋盘,打印棋盘

初始化棋盘,在没有下棋的时候,每个棋点都应该是空格。在游戏文件下写一个数组初始化的函数

//写在game.c内
//初始化棋盘  
void Initboard(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++)
		{
			board[i][j] = ' ';
		}
	}
}

 打印棋盘,打印棋盘就要知道棋盘是什么样子的,

大概就是这个样子。照葫芦画瓢,代码诞生:

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 < col -1)
            printf("___|___|___n");
    }
}

但是这个代码明显有局限性,就是只能打印三列。比如说某一天突然要来个五子棋,十字棋,那还要重新修改这段代码,而且被修改这段代码又长。现在对个代码进行优化:

void Displayboard(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++)
        {
            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");
    }
}

这样一个棋盘的初始化函数和打印函数就完成了。

3.游戏开始

游戏的过程无非就是玩家下棋,电脑下棋。就写两个函数,一个是玩家下棋的函数,一个是电脑下棋的函数,还是在game.c的文件下写。

void Player_move(char board[ROW][COL], int row, int col)
{
    printf("玩家下棋>");
    int x = 0;
    int y = 0;
    scanf("%d %d",&x,&y);
    while (1)
    {
        if( x >= 1 && x <= row && y >= 1 && y <= col)
        {
            if( board[x - 1][y - 1] ==' ')
            {
                board [x - 1][y - 1] = '*';//玩家下棋用'*'
                break;
            }
            else
            {
                printf("该坐标被占用 请重新选择n");
            }
        } 
        else
        {
            printf("输入错误,请重新输入n");
        }
    }
}
void Computer_move(char board[ROW][COL], int row, int col)
{
    int x = rand() % 3;//随机生成数字,取3的余数,总是0~2
    int y = rand() % 3;
    while (1)
    {
        if( board[x][y] ==' ')
        {
            board[x][y] = '#';//电脑下棋用'#'
            break;
        }
    }
}

这些代码看起来好像都没有错。但是,却跑不起来!因为电脑下棋的函数,随机生成的x,y进入到函数中就是固定的了!不符合条件的话一直循环!无法跳出。所以随机生成的数应该在循环内部。

void Computer_move(char board[ROW][COL], int row, int col)
{
    while (1)
    {
        int x = rand() % 3;//随机生成数字,取3的余数,总是0~2
        int y = rand() % 3;
        if( board[x][y] ==' ')
        {
            board[x][y] = '#';//电脑下棋用'#'
            break;
        }
    }
}

4.判断输赢

最后再写一个函数判断输赢。如果玩家赢了返回'*',如果电脑赢了返回'#',如果平局返回'p',如果游戏还在继续就返回'c'。

int is_full(char board[ROW][COL],int row, int col)
{
    int n = 0;
    int m = 0;
    for ( n = 0; n < row; n++)
    {
        for ( m = 0; m < col;  m++)
        {
            if ( board[n][m] == ' ')
            {
                return 0;
            }
        }
    }
    return 1;
}

char is_win( char board[ROW][COL],int row, int col)
{
    int i = 0;
    //判断行是否有3个相同的棋子
    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];
        }
    }
    //判断列是否为3个相同的棋子
    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];
        }
    }
    //判断对角线是否有三个相同的棋子
    for(i = 0; i < col; i++)
    {
        if( board[0][0] == board[1][1] && board[1][1] ==board[2][2] && board[1][1] != ' ')
            return board[1][1];
        if( board[2][0] == board[1][1] && board[1][1] ==board[0][2] && board[1][1] != ' ')
            return board[1][1];
    //判断是否平局
    if (1 == is_full())
        return 'p';
    return 'c';
    
}

判断输赢的函数就大功告成了。

5.完善游戏函数

所有要被使用的函数都已经写好,最后就该组装这些函数了。主要就是添加在test.c下的game()函数中。在此之前,应该把所有用到的函数都在game.h中声明一下,然后test.c文件引入game.h头文件,这样游戏里面的函数才能被正常调用。

//game.h下的全部代码

#include<stdio.h>
#include<time.h>
#include<stdlib.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);
void game()
{
    char board[3][3] = {0};//初始化一个数组用来存放数据
    //初始化棋盘
    Initboard(board,ROW,COL);
    //打印棋盘
    Displayboard(board,ROW,COL);

    //游戏开始
    while(1)
    {
    //玩家走
    Player_move(board,ROW,COL);
    Displayboard(board,ROW,COL);
    if ( is_win(board,ROW,COL) != 'c')
        break;

    //电脑走
    Computer_move(board,ROW,COL);
    Displayboard(board,ROW,COL);
    if ( is_win(board,ROW,COL) != 'c')
        break;
    }
    //判断输赢
    if ( is_win(board) == '*' )
        printf("玩家获胜");
    if ( is_win(board) == '#' )
        printf("电脑获胜");
    if ( is_win(board) == 'p' )
        printf("平局");
}

game()函数被完善好了!

6.完整代码

//game.h下的全部代码

#include<stdio.h>
#include<time.h>
#include<stdlib.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);
//test.c下的全部代码


#include"game.h"

void game()
{
	char ret;
	//数据存储到一个字符的二维数组中。玩家为’*‘,电脑为’#‘
	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");
	}
	if (ret == '#')
	{
		printf("电脑胜n");
	}
	if (ret == 'p')
	{
		printf("平局n");
	}
}


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


void test()
{
	srand((unsigned)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("离开游戏");
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}
	} while(input);
}


int main()
{
	test();
	return 0;
}
	
//game.c下的全部代码

#include"game.h"

//初始化棋盘
void Initboard(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++)
		{
			board[i][j] = ' ';
		}
	}
}

//展示棋盘
void Displayboard(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++)
		{
			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)
{
	printf("玩家下棋>");
	int x = 0;
	int y = 0;
	while (1)
	{
		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;
			}
			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;
		y = rand() % COL;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

int is_full(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;
}


//玩家胜返回'*',电脑胜返回'#',平局返回'p',继续返回'c'
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[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
		return board[1][1];
	//平局
	if (1 == is_full(board, ROW, COL))
		return 'p';
	//继续
	return 'c';

}

运行一下看看效果:

 基本上可以实现三子棋的功能,不过这里仍有不足,我们可不可以赋予电脑智能化呢?就是让电脑有自己的思想,可以和我们进行pk,而不只是单纯地随机生成数字下棋。这些下去我们仍需要思考,在这里就不做过多的解释了。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇

)">
下一篇>>