【C语言】三子棋小游戏(附完整代码)

【C语言】三子棋小游戏(附完整代码)

C语言实现简易三子棋小游戏

文章目录

    • 【C语言】三子棋小游戏(附完整代码)
  • 前言
  • 1.菜单的打印
  • 2.三子棋小游戏的构思
  • 3.三子棋的实现
    • 3.1使用宏的方式定义数组
    • 3.2打印棋盘
    • 3.3玩家下棋
    • 3.4电脑随机下棋
    • 3.5判断结局
  • 4.完整代码
    • 4.1 test.c
    • 4.2 game.c
    • 4.3 game.c


前言

提示:这里可以添加本文要记录的大概内容:

在了解C语言二维数组的相关知识后,我们可以应用一下二维数组的行列思维,完成三子棋小游戏。


提示:以下是本篇文章正文内容,下面案例可供参考

1.菜单的打印

我们在利用C语言完成一些小游戏项目的时候,往往需要在游戏开始前打印一个菜单,让玩家知道这是一个什么游戏,怎么玩这个游戏,如果玩家想要玩这个游戏,玩家应该怎样做等,同时也让这个小游戏的完成度更高,界面更加美观。我们可以通过封装一个菜单函数来实现这个功能。

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

此时控制台打印出来的效果如下:
【C语言】三子棋小游戏(附完整代码)_第1张图片
请选择后面的光标在等待我们输入值,接下来的一步就是:对玩家输入的值进行相应不同的处理玩家输入1,进入玩游戏的分支,玩家输入0,退出游戏当然,不排除一些来捣乱的玩家,输入不是1也不是0的内容,也就是非法值,这时我们可以使用swicth函数的default(默认)来应付。

当然,我们不只是每次都选择一次就结束掉程序,比如我玩一把游戏后,还觉得挺好玩,想再来一把,因此相应的就需要把这段代码写成一个循环才行。

在这个过程中,一运行程序,不管什么都让我们选择玩不玩游戏,这是循环里的循环部分,符合这种特性的循环是do while循环,一上来就执行一次循环体:我们思考while判断部分,在选择的时候,选择0退出游戏,选择1或选择输入非法值的时候,重新执行一次循环体。对应到循环,如果判断表达式是0,则退出循环,非0(1或非法值)则继续循环,我们可以得出使用input做循环的判断条件最合适了。

【C语言】三子棋小游戏(附完整代码)_第2张图片
以上为该项目的菜单模块

2.三子棋小游戏的构思

在写代码时之前,需要初步思考实现三子棋的方式。

【C语言】三子棋小游戏(附完整代码)_第3张图片
棋盘的固定格式,我们可以用循环来控制打印就可以了。三*三的棋子位置我们可以用二维数组的知识,玩家下棋或者电脑下棋都会改变数组元素,直到最后有一方胜出或者平局结束游戏。

通过简单的分析,我们需要创建一个三行三列的二维数组,这个数组起初初始化存放的值我们都初始化化为空格,因为打印出来相当于空包棋盘,接着就是实现玩家下棋的函数、电脑下棋的函数、判断输赢的函数。

3.三子棋的实现

3.1使用宏的方式定义数组

函数的声明、头文件的包含、宏的定义都放在.h文件里。.c文件里包含.h文件就可以正常使用.h文件里定义的标识符以及引用的头文件里的库函数。

【C语言】三子棋小游戏(附完整代码)_第4张图片
【C语言】三子棋小游戏(附完整代码)_第5张图片
【C语言】三子棋小游戏(附完整代码)_第6张图片

3.2打印棋盘

棋盘的打印是整个项目中比较难的部分。

  • 打印数据行
    【C语言】三子棋小游戏(附完整代码)_第7张图片
    “井”字有三行,二维数组也是有三行。其实棋盘是根据二维数组的大小来变换的。我们来看数据行,数据行是由两部分组成,一部分是:空格+%c+空格,另一部分是|(竖杠)。

【C语言】三子棋小游戏(附完整代码)_第8张图片
【C语言】三子棋小游戏(附完整代码)_第9张图片
在了解数据行的打印后,我们再来看分割行的部分(—|—|—)

【C语言】三子棋小游戏(附完整代码)_第10张图片
【C语言】三子棋小游戏(附完整代码)_第11张图片我们可以发现,打印分割行的时候,最右边的 | 还是不用打印,就像在数据行中,用 — 代替空格+%c+空格,| 还是 | 一样。

好了,到这里我们的棋盘打印就讲完了,这时如果我们改变标识符ROW、COL的值,棋盘的大小也会随之改变。由此可以得到想要的五子棋、十子棋等等。

3.3玩家下棋

下棋就是把二维数组里的元素改成玩家用的棋子(字符)就行,这样,打印的时候,就相当于数组位置放了棋子一样。电脑下棋也是同理。

【C语言】三子棋小游戏(附完整代码)_第12张图片
对于二维数组而言,访问下标是从0开始的,因此二维数组的访问范围为x(0-2),y(0-2)。代码中我们可以看出我们使用了x-1,y-1,这是因为在游戏的设计中,要考虑到玩家的使用习惯,在玩家输入(1,1)时必须对应二维数组下标中的(0,0)才行。此时坐标范围为x(1-3),y(1-3)。

3.4电脑随机下棋

一般来说,成型的三子棋游戏,电脑并不是随机下棋的,而是根据棋盘情况选择性的下棋,对玩家而言更有挑战性。但是为了实现代码的简单性,这里我们实现的是电脑的随机下棋。

【C语言】三子棋小游戏(附完整代码)_第13张图片

我们先看一下test.c文件里的调用接口函数情况。
【C语言】三子棋小游戏(附完整代码)_第14张图片
玩家下棋和电脑下棋应该是循环交替进行的,直到游戏结束,即判断输赢或平局才终止,我们加上while循环。
【C语言】三子棋小游戏(附完整代码)_第15张图片
在需要终止循环的时候,我们可以定义一个判断输赢的函数IsWin(),在有一方赢了或者棋盘满了后执行退出循环。

3.5判断结局

那我们如何设计判断游戏的结局呢,根据三子棋游戏的规则,要有一方的获胜的话,需要达到,横三列、竖三列或者是两条对角线都是其中一方的棋子才行。如果棋盘满了,也就是达到了平局的条件。
我们如何将某一方获胜或平局或棋盘还没到结局需要继续下棋的状态告诉主调函数game()呢,我们可以使用如下的返回值:

  • 某一方赢了就返回赢的条件里任意一个位置的棋子。
  • 棋盘没有满,返回字符’C’告诉主调函数说“继续下棋”。
  • 棋盘满了,返回字符’F’说明棋盘满了。
    我们来看一下主调函数部分:

【C语言】三子棋小游戏(附完整代码)_第16张图片
再来看一下实现判断结局部分:

横三行和竖三行:
【C语言】三子棋小游戏(附完整代码)_第17张图片

对角线:
请添加图片描述

判断棋盘上棋子有没有满的函数IsFull():
【C语言】三子棋小游戏(附完整代码)_第18张图片

【C语言】三子棋小游戏(附完整代码)_第19张图片

主调函数game()的完善:
【C语言】三子棋小游戏(附完整代码)_第20张图片

到这里,这个游戏就基本上完成啦,我们玩一把看一下效果吧~

【C语言】三子棋小游戏(附完整代码)_第21张图片
【C语言】三子棋小游戏(附完整代码)_第22张图片
对于这个三子棋小游戏我们还可以进行一些改进,比如改成五子棋,十子棋,还可以进行一些游戏化界面设计等等,增加更多趣味性。

4.完整代码

4.1 test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include"game.h"

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

void game()
{
	char board[ROW][COL] = { 0 };
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	char ret = 0;//接收返回值
	while (1)
	{
		//下棋
		//玩家下棋
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')//如果ret不是C,说明游戏已经有结果了
			break;
		//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢	
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')//这里就体现了返回赢方胜利条件的某一位置元素的好处了。
		printf("玩家赢\n");//比如玩家第一行连成三子,IsWin返回其中一个棋子,代表使用这个棋子的一方获胜。
	else if (ret == '#')
		printf("电脑赢\n");
	else
		printf("平局\n");
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n\n");
			game();
			break;
		case 0:
			printf("退出游戏");
			break;
		default:
			printf("请不要捣乱哦,选择错误,重新选择\n");
			break;	
		}
	} while (input);
	
	return 0;

}

4.2 game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include"game.h"

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

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//控制行
	{
		//1.打印数据
		int j = 0;
		for (j = 0; j < col; j++)//有col列
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)//数据行的分割符列数少1,也就是col-1
			{
				printf("|");
			}
		}
		printf("\n");//打印完数据行换行
		//2.打印分割符
		if (i < row - 1)//分割行是行数减1,也就是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;
	printf("玩家下棋:>\n");
	while (1)
	{
		printf("请输入下棋的坐标,中间使用空格>:");
		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;//玩家成功落了一枚棋子*,就不能还是玩家下了,因此break跳出
			}
			else//不能落子
			{
				printf("坐标被占用,不能落子\n");
			}

		}
		//坐标非法
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

//电脑随机下棋
//范围0~2即可
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;
		}
	}
}


int IsFull(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++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	//遍历完,所有元素都不是空格,说明已经下满了,返回1
	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][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[i][0];
		}
	}
	//对角线
	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];

	//平局
	if (IsFull(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

4.3 game.c

#pragma once

#include
#include
#include

#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);


//判断输赢
//玩家赢--'*'
//电脑赢--'#'
//平局--'Q'
//继续--'C'

char IsWin(char board[ROW][COL], int row, int col);


你可能感兴趣的:(C,c语言)