C语言三字棋小游戏详解

Posted Persistence_i

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言三字棋小游戏详解相关的知识,希望对你有一定的参考价值。


前言:今天我们来用C语言写一个三子棋小游戏,三子棋,是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。这里我们写的是玩家与电脑对战的一个代码,如果想跟朋友一起玩也可以自己稍作修改噢。

让我们往下看吧

目录:

一、想法与构思

1.想法

首先想要做一个游戏就先明白游戏的规则和玩法,三子棋,就是在一个九宫格里面下子,两个人对战,每一个人每一轮下一子,当任意一方有横竖斜其中一种三子连线,即该方获胜。若九宫格填满,仍无连线,则为平局。

没错,这就是我们想写出来的游戏,那么接下来就是构思了↓

2.构思

有了想法构思就很容易啦。
首先我们要确定我们代码中要实现的:

  1. 在初始状态下出现一个九宫格。
  2. 玩家和电脑轮流进行输入。
  3. 每一轮可在里面输入一个字符标记。
  4. 判断游戏继续、平局或谁胜谁负。

完成构思接下来就进入我们的代码领域了。



二、代码与实现

1.代码分块

为了让整个代码更加可观,我们首先把代码分为三个板块:
1.test.c源文件
测试游戏和一些细节的补充。

2.game.h头文件
存放游戏函数的声明。

3.game.c源文件
整个游戏的实现。

2.test.c

首先我们来将代码的整体框架给写出来:

//先写一个菜单函数,让我们选择是否进入游戏
void menu()
{
	printf("***********************************\\n");
	printf("***********   1. play     *********\\n");
	printf("***********   0. exit     *********\\n");
	printf("***********************************\\n");
}

void game()//游戏主体的代码
{

}

int main()//主函数
{
	int input = 0;
    do
	{
		menu();//引用上面的菜单函数
		printf("请选择>");
		scanf("%d", &input);//选择是否进入游戏
		switch (input)
		{
		case 1://输入1进入游戏
			game();
			break;
		case 0://输入0退出游戏
			printf("退出游戏\\n");
			break;
		default://输入其他会提示输入有误
			printf("输入有误\\n");
			break;
		}
	} while (input);

	return 0;
}

然后我们要补充好game()的部分:

按照构思,我们先要把初始化的棋盘给写出来,那初始化棋盘如何写呢,一个九宫格的棋盘可以看作为九个空格和一个井字合起来,而且空格中需要输入东西,所以我们可以用一个二维数组了表示,如何横竖的井字,我们可以用符号" — " 和符号 " | "在二维数组的缝隙中接入。

那么直接写?不不不,还记得我们区分板块吗,在这里我们只需要命名几个函数,然后在game.c上写就可以啦

void game()//三子棋游戏的过程
{ 
	char board[ROW][COL];//创建一个二维数组作为棋盘
	//ROW为行,COL为列
	
	InitBoard(board, ROW, COL);//初始化棋盘 - board的元素都给成空格
	//将二维数组和行列传进函数
	
	DisplayBoard(board, ROW, COL);//打印横竖的棋盘
	//将二维数组和行列传进函数
}

然后接下来就是下棋的部分了。
再看回我们的构思,玩家和电脑轮流下棋。所以我们得写两个函数分别描述玩家下棋电脑下棋

void game()//三子棋游戏的过程
{ 
	char board[ROW][COL];
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	
	PlayerMove(board, ROW, COL);//玩家下棋
	DisplayBoard(board, ROW, COL);//打印玩家下棋后的棋盘
		
	ComputerMove(board, ROW, COL);//电脑下棋
	DisplayBoard(board, ROW, COL);//打印电脑下棋后的棋盘
}

然后我们需要是第三个构思。每一轮! 我们不可能一轮就分出胜负,所以我们要在玩家和电脑下棋之间放一个循环,同时我们再看第四个构思,当循环出来那么就是胜负已分了。

所以我们要在game()中添加一个循环和一个胜负条件还有平局条件:

这里我们可以设置一个判断胜负函数 IsWin,然后 给 IsWin函数设置返回值,结果为

  1. 如果胜负未分,游戏继续,则返回值为C(continue)
  2. 如果玩家赢了,返回玩家的符号,反之返回电脑的符号。
  3. 什么都不返回就平局。
void game()//三子棋游戏的过程
{ 
	char board[ROW][COL];
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	char ret = 0;
	while (1)//设置循环,当判断平局或胜负就跳出
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		 
        //设置一个判断胜负函数 IsWin
        //在这里设置一个变量ret,接收IsWin返回的结果
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;//如果不等于C(continue),就是平局或胜负已分
			      //直接跳出循环
		}
		
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;//同上
		}
	}
//跳出循环后判断接收到的返回值
	if (ret == '*')//假设玩家使用的是*
	{
		printf("游戏结束,玩家获胜!\\n");
	}
	else if (ret == '#')//电脑使用的是#
	{
		printf("游戏结束,电脑获胜!\\n");
	}
	else//如果上面两个都不是,且退出游戏了,那就是平局了。
	{
		printf("游戏结束,平局!\\n");
	}
}
    

以上就是我们test.c的代码。


3.game.h

写好主函数之后,那么头文件game.h中的函数声明就很容易写好了。
部分说明直接在代码中注释就可以了。

#pragma once    //作用:让引头文件只引一次,防止头文件被多次包含。
//在这里我们几个模块需要的头文件的放这里就可以了
//到时候再在另外两个中直接引用 "game.h"就可以使用game.h中引用的头文件了。

#include <stdio.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 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);

4.game.c

重头戏来了,整个代码的灵魂,也是最难写的部分来了。

前面写了那么多也仅仅是把构思的框架实现了而已,这里才是真正把游戏写出来的地方。

那我们一个一个来吧:

1.InitBoard(初始化九宫格棋盘)

噢,放错了。应该是这个:

这里的话,初始化的时候,九宫格中都是空的,但是后续我们还要往里面填进玩家和电脑输入的符号,所以我们暂且要用空格表示。

代码:

void InitBoard(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++)//列的循环
		{
			board[i][j] = ' ';
		}
	}
}

2.DisplayBoard(横竖划分)

在这里,我们可以先想像棋盘是什么样子的:

那我们打印的时候呢(要用 — 和 | ):

我们可以看到先 — 再 | ,而且是三次打印 — 两次打印 |

所以整体是分为:

  1. 打印玩家或电脑输入字符+竖杠分割线
  2. 横杠分割线+竖杠分割线

代码:

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 < row - 1)//打印两次
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)//比横少一次打印
					printf("|");
			}
		}
		printf("\\n");
	}
}

3. PlayerMove(玩家进行游戏)

首先我们要提示到玩家进行游戏,然后为了统一和简化玩家操作,我们直接把玩家想画的地方改为坐标指示。比如1 1就是画第一行第一列。

然后我们也要判断玩家输入的坐标是否有效,要考虑坐标存不存在坐标里面是否已经被画

代码:

void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("轮到玩家进行操作:>\\n");

	int x = 0;
	int y = 0;
	while (1)//设定一个死循环,直到玩家输对坐标才跳出去
	{
		printf("请输入坐标:>");
		scanf("%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");
		}
	}
}

4.ComputerMove(电脑进行操作)

电脑操作就比玩家简单多了,直接设定一个符合坐标的随机值,再判断这个坐标为空格就可以了。

代码:

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;
		}
	}
}

5. IsWin(判断胜负)

判断胜负的条件也很简单,我们可以分别写出来:

  1. 横上有三个相同的符号
  2. 竖上有三个相同的符号
  3. 对角线有三个相同的符号

代码:

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[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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

}

然后还有平局和继续游戏这两个情况还没有,我们可以写一个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 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[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[0][2] == board[1][1] && board[[C语言]三字棋游戏

C语言初期学习遇到的特殊点 三子棋详解初学者福音,详细总结,复习能手

多文件实现三子棋

C语言实现三子棋小游戏(注释详解)

C语言小游戏-三子棋详解

三子棋(C语言实现)