当前位置:首页 » 《随便一记》 » 正文

用c语言实现三子棋小游戏_HandsomeDog_L的博客

23 人参与  2021年11月16日 17:43  分类 : 《随便一记》  评论

点击全文阅读



目录

前言

本文适合有一定c语言基础的小伙伴,基础稍微薄弱的可能看着会有点吃力,但并不会影响对思路的把握。这类型的程序要求有一些基础,它可以体现你对所学知识的整体掌握程度  在阅读本文前,希望你们已经掌握了以下知识包括   头文件 源文件定义及作用    函数及函数传参函数传参    循环  数组(二维数组)

正文

引入

代码程序化

1.菜单 menu()

2.棋盘 Board()

3.下棋  Move()

4.判断规则或者叫游戏规则  Judge()

后话




前言


本文适合有一定c语言基础的小伙伴,基础稍微薄弱的可能看着会有点吃力,但并不会影响对思路的把握。这类型的程序要求有一些基础,它可以体现你对所学知识的整体掌握程度  在阅读本文前,希望你们已经掌握了以下知识
包括   头文件 源文件定义及作用    函数及函数传参函数传参    循环  数组(二维数组)

本文将展示写程序的思路,以及代码实现



正文

引入

三子棋应该具有哪些要素?或者说你想设计一个小游戏,你希望它有什么功能呢?

以三子棋为例,我觉得应该有以下的功能

1.菜单

2.棋盘

3.玩家和电脑下棋

4.判断规则

ok,功能已经想好了,那就开始把它们用程序写出来


代码程序化

1.菜单 menu()

代码如下:

void menu()
{
    printf("************三子棋游戏************\n");
    printf("****** 1. play *** 0. exit ******\n");
    printf("*********************************\n");

}菜单可以自己设计,你觉得好看就行


2.棋盘 Board()

你觉得棋盘有该有哪些功能?

我觉得应该有初始化功能 InitializeBoard()   和    显示功能DisplayBoard()

于是我们有了以下代码

     //用数组来存放我们要下的棋子
    char Board[ROW][COL] = { 0 };
//棋盘初始化   数组里面的元素为空格,视觉上看起来棋盘是空的
void InitializeBoard(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++)
    {
        int j = 0;
        for (j = 0; j < col; j++)//打印第i行的内容
        {
            //打印数据行
            printf(" %c ", Board[i][j]);//对应下图的1号绿色圆角方框
            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");
        }
    }
}看到这里可能有小伙伴会对DisplayBoard()的写法有点疑问,希望下面的这张图能打消你的疑虑

左边红色的椭圆代表Board[i][j],右边的1号绿色圆角方框代表"    Board[i][j]      "对应代码的  " %c "(注意中间空白是一个空格哦)    2号方框代表竖着的分割线 对应代码中的    "|"  ,那么3号方框代表什么应该知道了吧,没错,就是代码中的"---"

为什么要这么设置呢?那是因为这才是打印出棋盘的最小"循环单元"这里就不细述原因了,毕竟这不是代码的重点.如果有疑惑的话可以私信我哦

3.下棋  Move()

有两个玩家(玩家和电脑)那就自然有两个函数啦

玩家下棋PlayerMove()   和  电脑下棋ComputerMove()

因为我们在上面已经将棋盘的元素初始化为空格了所以要想下棋的话 就把棋盘上对应坐标的空格替换成代表玩家棋子的字符  "*"   "#"就行啦,这里呢我们用"*"代表玩家的棋子,"#"代表电脑的棋子,当然你想用什么表示就用什么表示,只要是个字符就可以了.

void PlayerMove(char Board[ROW][COL], int row, int col)
{
	//用坐标来表示位置
	int x = 0;
	int y = 0;
	printf("玩家走\n");
	while (1)//考虑到玩家有可能会不小心输入错误坐标,so 应该设计while循环
	{
		printf("请输入要下的坐标:  ");
		scanf_s("%d%d", &x, &y);
		//判断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 ComputerMove(char Board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑走\n");
	while (1)
	{
		//利用rand函数不断产生随机数
		x = rand() % row;
		y = rand() % col;
		if (Board[x][y] == ' ')//电脑他知道数组定义啊,so 这里直接没有减一
		{
			Board[x][y] = '#';
			break;
		}
	}

}

4.判断规则或者叫游戏规则  Judge()

三子棋三子棋,顾名思义就是三个子的棋(你小子搁着搁着呢!) 开个玩笑

判断输赢规则无非就是四条   三行三列两对角,只要这四条线上的棋子一致就赢了

当然还应该有个函数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;//满了
}
char Judge(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 == IsFull(Board, ROW, COL))
	{
		return "Q";
	}
	//继续
	return 'C';
}

三行三列的判断上我觉得我们写的代码应该差不多

但是在两对角线的写法上有小伙伴会说,既然三行三列都可以用循环表达,那么两对角线应该也可以

for (i = 0; i < row; i++)//左上-右下
	{
		if (Board[i][i] == Board[i + 1][i + 1])
			continue;
		else
			break;
	}
	for (i = 0; i < row; i++)//左下-右上
	{
		if (Board[i][row-1-i] == Board[i + 1][row -2-i])//当i=2的时候数组会越界
			continue;
		else
			break;
	}

对,思路没问题,只是会产生越界的情况,那可不可以避免呢?答案是可以.只要在定义ROW和COL的时候,多加个2,比如,这个程序是三子棋,ROW,COL为3,只要定义它俩为5,让整个棋盘多一圈,我们在对Board[][]数组进行操作时只操作其下标中的1-3就行了.这里就只给出思路了,感兴趣的小伙伴可以自己尝试一下,我相信你要是能尝试下去就可以摸索出n子棋的写法.

全局代码如下

有三个板块,一个头文件game.h,两个源文件test.c   game.c

game.h

//函数声明部分

#define ROW 3
#define COL 3

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//声明
void InitializeBoard(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);


//告诉我们四种游戏的状态
//玩家赢 - '*'
//电脑赢 - '#'
//平局   - 'Q'  用单字符表示嗷,不要为了显示你的高英语水平就用字符串,越简洁越好
//继续   - 'C'

char Judge(char Board[ROW][COL], int row, int col);

game.c

//游戏模块  函数定义

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


void InitializeBoard(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++)
	{
		int j = 0;
		for (j = 0; j < col; j++)//打印第i行的内容
		{
			//打印数据行
			printf(" %c ", Board[i][j]);//对应下图的1号绿色圆角方框
			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 PlayerMove(char Board[ROW][COL], int row, int col)
{
	//用坐标来表示位置
	int x = 0;
	int y = 0;
	printf("玩家走\n");
	while (1)//考虑到玩家有可能会不小心输入错误坐标,so 应该设计while循环
	{
		printf("请输入要下的坐标:  ");
		scanf_s("%d%d", &x, &y);
		//判断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 ComputerMove(char Board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑走\n");
	while (1)
	{
		//利用rand函数不断产生随机数
		x = rand() % row;
		y = rand() % col;
		if (Board[x][y] == ' ')//电脑他知道数组定义啊,so 这里直接没有减一
		{
			Board[x][y] = '#';
			break;
		}
	}

}

//判断棋盘满没满
//返回1表示棋盘满了
//返回0,表示棋盘没满

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;//满了
}
//三行三列两对角
char Judge(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 == IsFull(Board, ROW, COL))
	{
		return "Q";
	}
	//继续
	return 'C';
}

test.c

//来测试我们的三子棋程序  测试模块
#include "game.h"
void menu()
{
	printf("************三子棋游戏***********\n");
	printf("****** 1. play *** 0. exit ******\n");
	printf("*********************************\n");

}
void game() 
{
	//存放Judge()函数的返回值
	char ret = 0;
	//用数组来存放我们要下的棋子
	char Board[ROW][COL] = { 0 };
	//棋盘初始化   数组里面的元素为空格,视觉上看起来棋盘是空的
	InitializeBoard(Board,ROW,COL);//因为要使用这个初始化函数,就得声明,而初始化函数是game函数的一部分,所以我们应该在 game.h 这个头文件中对其进行声明
	//打印棋盘
	DisplayBoard(Board, ROW, COL);
	//下棋,也就是"填空",把已经初始化的棋盘上的   ' '   替换成玩家和电脑要走的棋子
	while (1)
	{
		//玩家走
	    PlayerMove(Board, ROW, COL);
		DisplayBoard(Board, ROW, COL);
		
		ret=Judge(Board, ROW, COL);
          //判断玩家是否赢
		if (ret != 'C')
		{
			break;
		}
		//电脑走
		ComputerMove(Board,ROW,COL);
		DisplayBoard(Board, ROW, COL);
		//判断电脑是否赢
		ret = Judge(Board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	
	}
     //判断剩下三种情况
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}
}
void test()
{ 
	int choice;
	srand((unsigned int)time(NULL));//时间戳,配合rand函数
	do
	{
		menu();
		printf("请选择:\n");
	   
		scanf_s("%d", &choice);
		switch (choice)
		{
		case 1:
			printf("开始游戏\n");//system clc
			game();
			break;
		case 0:
			printf("退出游戏");
			break;
		default:
			printf("输入无效,请重新输入\n");

		}

	} while (choice);


}


int main()
{

	test();
	return 0;
}

这个是三个文件创建好后的亚子 

 最后附上我和电脑之间的"博弈"



后话

可能有人会说要是可以鼠标点击就好了,点哪里就落子哪里,毕竟这是一个小程序,而不是一个正

真意义上的游戏,而且编者水平有限,才刚开始学,实现起来有点困难,今天埋下一个伏笔,等我

学得差不多了,我就来补上


如果有想交流的可以评论或者私信


最后希望各位小伙伴在做类似的综合性比较高的题时可以画思维导图,好处  思路清晰,不会去想

一步写一步,时刻告诉自己这一步该做什么,我要实现什么功能

全文一共八千八百多个字,看了这么久屏幕要注意保护眼睛哦

回见!


点击全文阅读


本文链接:http://zhangshiyu.com/post/30949.html

棋盘  数组  函数  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1