C语言实现井字棋

C语言实现井字棋

序言:
这是真的很简略的说明,井字棋,大家都玩过吧,上课跟同桌打发时间最好用的东西,那么为了找回童年的回忆!咱们开始用咱们大人的方式!设计一个吧!
简要说明:
井字棋,物如其名!它就是个井字作为棋盘,然后进行填格子的小游戏,那么,实现方法就很明确啦,因为是井字,所以是3x3的小格子组成,那么我们熟悉的二维数组它又又来了!(好烦!!)既然棋盘有了眉目,那剩下的就是下棋和胜利方式了呗~~下棋的方法,因为不会只下一次,所以我们会用到循环,来回下,往死里下!(靠谱!!)咳咳咳,然后呢,就是胜利的方式了,不就是个三个相同的棋子在一条直线或者两条对角线上嘛,很好!!!!就这样!我们开始叭!

那么我们直接开始叭!这次比较简单哦!

函数调用模块:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void game()
{
	char ret = 0;
	//创建棋盘
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);
	while (1)
	{
		//玩家下棋
		Playermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断玩家赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		Computermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断电脑赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("You Win!\n");
	}
	else if (ret == '#')
	{
		printf("You Lose!\n");
	}
	else
	{
		printf("No One Win!\n");
	}
}
void menu()
{
	printf("********************************\n");
	printf("*********  1.开始游戏  *********\n");
	printf("*********  0.退出游戏  *********\n");
	printf("********************************\n");
}
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;
}

咳咳咳,主函数这么定义完全是因为我个人喜好!感觉这么好看点!下次一定改!!(下次一定!下次一定!)首先我们看到我们的主函数中的唯一的一个函数,也是我们的熟悉的菜单,玩游戏嘛,总得给玩家一个选择,首先我们看到了一个奇怪的东西srand这是个什么函数,不懂得小伙伴可以戳链接,咱特地为它写了一篇说明!看懂了记得回来继续哟~~(链接:srand和rand的介绍)那,看完介绍!大家都明白了叭~那咱们继续咯!下面的就是咱们的菜单函数啦,很常见,很平常,不多解释啦,然后是switch选择语句,这是给玩家选择模式用的(这种基础函数比如switch和do while等等这些基础的函数,咱会出详细介绍的博文的!这里不做多余的解释啦!咱会加油的!!)使用do while循环的作用是,在不进行游戏时,可以让菜单循环跳出,供玩家选择,这里的小技巧就是,while的判断条件是玩家输入的数字input,这里给家的的就是一个小技巧,因为while只会在判断条件为假(也就是括号内为0)的时候结束循环,那么我可以利用这个特性,来循环咱们菜单选择界面!达到想玩就继续玩,不想玩就退出游戏的效果!
那么!咱们转到咱们的重点地方!game函数这是实现咱们游戏的核心函数,也是最需要说明的地方!首先其中的打印,初始化棋盘函数的声明都有注释~大家要自己看哟!(因为咱打字打的手手痛!!偷个懒QAQ),game函数中,主要是提供了我们玩游戏的棋盘下棋的方法电脑下棋的方法判断输赢的方法这四部分。那么我们开始具体的来看看它们是如何实现的叭!!

头文件模块:

#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 Playermove(char board[ROW][COL], int row, int col);
void Computermove(char board[ROW][COL],int row, int col);
//1.玩家赢 "*"
//2.电脑赢 "#"
//3.平局了 "Q"
//4.继续   "C"
char IsWin(char board[ROW][COL], int row, int col);

头文件这里呢,define是个什么东西?这个东西叫做,你可以把它也叫做程序猿偷懒专用方法!也是比较方便的编程方法,等一下大家会有体会的,来介绍一下它的功能,详细介绍会在另外的文章中为大家解答!#define ROW 3这就是定义了一个宏,它的作用是为这个ROW变量赋予了它特定的意思,是程序猿自定义的,即ROW = 3,以后ROW不管在哪里出现都可以视作整形数字3下面的同理。那么为什么要把库的声明,函数声明都放在这里呢,这也是一个现阶段大家碰不到,以后可能会碰到的情况,一个大型软件的研发,往往不是一个人完成的,是一个小组或者很多个小组完成的,那么每个人写的代码可能某一个函数,某一个模块,如果没个人都在程序开头写一个#include<stdio.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) //打印棋盘
{
	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 < col - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}

看到这里,就会出现:(看!!这个博主是个呆瓜!!还要给棋盘多写一个打印函数,真费事!!!)
咳咳咳,你看,人和人都不一样,玩家跟玩家的思路肯定也不一样,总有一些变态的人想去玩横横横横竖竖竖竖棋,我们为了满足他们的变态要求!就出现了这个!棋盘打印函数!专门打印棋盘样式!可以实现逐格打印~这样就可以满足他们的要求!咱们首先看到的是一个双循环,第一层循环不仅嵌套了一个循环还有一个if语句我们可以看到它的循环条件col - 1(这里col = 3,不知道为什么的小伙伴可以去看看头文件的介绍哟),再看里面的内容,奥,原来是用来分割棋盘的,那么为什么要减 1 呢?大家思考一下,井字棋是不是只有两个横线两个竖线第三行咱们是不需要的横线的所以减 1 ,现在,咱们看第二层循环内实现的是什么,先是打印我们刚才初始化完成的数组元素,然后打印一个竖线,这样一行下来就是数组元素 | 数组元素 | 数组元素为什么只有两个竖线呢,细心的同学已经看到啦,咱们的if语句的判断条件是j < col - 1这样在第三次数组元素打印完就不会再打印竖线啦!这样,咱们的棋盘打印函数就介绍完咯!接下来看一下效果图!
在这里插入图片描述

下面就是玩家和电脑之间的博弈啦!

void Playermove(char board[ROW][COL], int row, int col) //玩家下棋
{
	int x = 0;
	int y = 0;
	printf("请玩家下子!\n");
	while (1)
	{
		printf("请输入坐标:");
		//判断x,y坐标的合法性
		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("该坐标已被占用");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入!\n");
		}
	}
}

这个是实现玩家如何下棋的函数!首先咱们创建连个变量,是用来存放玩家落子的坐标的,使用while的原因是,因为玩家一次下子有多种可能,可能下在了已经下过的地方呀,也可能坐标输入不合法,所以需要多次输入那么就需要while循环来扛起大旗!咱们看看循环体是什么,首先输入坐标,然后进行判断,先判断是否合法,那么为什么要 >= 1而不是 **>= 0 **(后面的row和col同理)这个很快咱们就有解释,再往下看,这个 if 的判断条件就很有趣了,为什么要给输入的数组下标减 1 呢,那多次一举,为啥不给第一次判断地址是否和法就变成col -1 和 y >= 0呢,这是因为,玩家不一定都是程序猿啊,他们又不知道数组首元素下标是 0 只有接触过编程的人才知道这种东西,所以这种设计就是为了亲民!也算是个小细节叭! 第三个 if 判断的是走的位置是不是已经下过子了,如果是空格,就往里面落一个子,也就是一个“ * ” ,反之就是打印那句话,然后重新下子,落子成功后,break跳出循环,结束函数,这就是玩家下棋的流程!那么继续往下看吧!

void Computermove(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;
		}
	}
}

这个呀~是电脑下棋的实现啦,这个就很简单了,用到了咱们开始讲的rand函数提供随机值,并且模了一个row,也就是模3任何数模3最后的产生的值是0 - 2之间的数,大家可以去试试~然后进行和玩家相同的判断,就可以实现啦,这里比较难的就是rand函数的使用,不是很清楚的同学在函数调用模块种就有rand函数的讲解链接哦!那继续咯!

//返回1满了
//返回2没满
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 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][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[0][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';
}

现在就是胜负判断函数的实现啦!,这个看着多,其实很好理解的,大家不要害怕,仔细看就能看懂的,咱们先看IsWin函数,用了两个 for 循环五个 if 判断语句而已,咱们开始也提到过,井字棋的输赢就是看每一行是否有三个相同的棋子或者两条对角线线上是否有三个相同的棋子,那么这个 if 语句的判断条件就是显而易见了叭!就只是将二维数字的每一行的元素和两条对角线上的元素做了一个相等的判断而已,然后这个返回值,就是代表我们玩家的棋子字符或者是代表电脑的棋子字符,最后只需要判断是哪个字符,然后打印相应的提示语,就可以很简单的达到判断输赢的要求,那么除了,胜利和失败,肯定还有平局叭,那么平局的条件是什么呢,是不是棋盘满了,并且没有一行或者一条对角线上有玩家或者电脑的棋子达到三个相同就是平局。那么请看上面的IsFull函数(这里稍微运用了一下函数的嵌套调用,再一个函数中遇见另外一个函数,程序会去将该函数执行完毕在返回刚才的位置继续执行)它就是用来判断棋盘是否下满的函数,也是很简单,跟初始化相同,将数组中每一个元素都跟字符空格相比较,有相同的就是没下满,没有相同的就是下满了,然后返回相应的返回值,然后返回到现在的 if 判断语句中,返回相应的返回值。那么返回的字符如何判断呢,请看下面的代码:

while (1)
	{
		//玩家下棋
		Playermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断玩家赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		Computermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断电脑赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("You Win!\n");
	}
	else if (ret == '#')
	{
		printf("You Lose!\n");
	}
	else
	{
		printf("No One Win!\n");
	}
}

首先玩家下棋,打印棋盘,来显示玩家下棋的位置然后将玩家下完的棋盘数组传给咱们的胜负判断函数进行判断(电脑下棋同理),ret变量接收返回来的值,如果是字符 C 则游戏继续,如果其他字符跳出循环,来进行相应的判断,判断玩家的胜负或者平局!这样我们就实现了我们的井字棋小游戏,整体并不难,没有什么重难点,有一些零星的小细节需要大家注意,相信大家静下心思考,都能写出来这样的甚至是更加完善的井字棋小游戏!

结束语:

到这里,我们的井字棋小游戏的设计思路,程序实现都展示完啦,到底还是为了强化对二维数组的理解,相信大家都可以做的更好!大家加油!!!!

源代码在这里!

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma once
#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);
//1.玩家赢 "*"
//2.电脑赢 "#"
//3.平局了 "Q"
//4.继续   "C"
char IsWin(char board[ROW][COL], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void game()
{
	char ret = 0;
	//创建棋盘
	char board[ROW][COL] = { 0 };
	//初始化棋盘
	Initboard(board, ROW, COL);
	//打印棋盘
	Displayboard(board, ROW, COL);
	while (1)
	{
		//玩家下棋
		Playermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断玩家赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		Computermove(board, ROW, COL);
		Displayboard(board, ROW, COL);
		//判断电脑赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("You Win!\n");
	}
	else if (ret == '#')
	{
		printf("You Lose!\n");
	}
	else
	{
		printf("No One Win!\n");
	}
}
void menu()
{
	printf("********************************\n");
	printf("*********  1.开始游戏  *********\n");
	printf("*********  0.退出游戏  *********\n");
	printf("********************************\n");
}
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;
}
#define _CRT_SECURE_NO_WARNINGS 1
#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)
{
	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 < col - 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)
	{
		printf("请输入坐标:");
		//判断x,y坐标的合法性
		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("该坐标已被占用");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入!\n");
		}
	}
}

void Computermove(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;
		}
	}
}
//返回1满了
//返回2没满
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 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][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[0][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';
}
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值