IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言实现三子棋游戏 -> 正文阅读

[C++知识库]C语言实现三子棋游戏

前言

这是我作为C语言入门者也是一名大一新生的第一篇博客,在此之前也经过了一小段时间的学习,成功写出了一个简易的三子棋小游戏,如有需改正之处,望大家批评指正。


思路

整个游戏的代码我计划分为两个源文件实现,第一个源文件test.c用于实现整个游戏的大体流程,第二个源文件game.c用于实现游戏具体步骤的逻辑,也因此需要一个头文件game.h来简化game.c中的函数在test.c中的声明。

在打开游戏时需要打印一份菜单供玩家选择开始游戏还是结束游戏,当玩家完成一盘游戏后,还需要询问玩家是否需要重新开始,因此可以通过循环来实现。

在游戏过程中,流程为打印空棋盘-->玩家下棋-->打印棋盘-->电脑下棋-->打印棋盘-->玩家下棋-->打印棋盘-->电脑下棋-->打印棋盘……循环至一方连成三颗棋或是棋盘被完全占用时,游戏结束。


游戏实现

? ? 头文件game.h

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

//定义棋盘大小,可修改
#define ROW 5
#define COL 5

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

头文件game.h的作用在于声明函数、包含系统头文件、以及定义常量。

声明函数:简化在tect.c中声明game.c中函数的过程

包含系统头文件:两个源文件中只需包含game.h即可

定义常量:只需在game.h中改变常量即可改变棋盘大小


? ? 源文件test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void menu()
{
	printf("******************************\n");
	printf("**********1.开始游戏**********\n");
	printf("**********0.结束游戏**********\n");
	printf("******************************\n");
}

void game()
{
	char board[ROW][COL];
	char ret;
	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("玩家获胜\n");
	else if (ret == '#')
		printf("电脑获胜\n");
	else
		printf("平局\n");
}

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do//初始菜单
	{
		menu();//打印菜单
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("结束游戏");
				break;
			default:
				printf("输入错误,请重新输入");
				break;
		}
	}while (input);
	return 0;
}

? ? ? ? 1.main函数

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do//初始菜单
	{
		menu();//打印菜单
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
			case 1:
				game();
				break;
			case 0:
				printf("结束游戏");
				break;
			default:
				printf("输入错误,请重新输入");
				break;
		}
	}while (input);
	return 0;
}

在main函数中我们需要实现游戏的大体流程。

因为需要允许多次游玩,并且至少游玩一次(开始游戏或结束游戏都算),所以使用do while循环。

此处使用变量input接受玩家菜单页面选择结果,可直接使用input作为do while循环的判断条件(0为假,非0为真)。

switch语句的使用是为了更加直观,也可使用if语句。


? ? ? ? 2.menu函数

void menu()
{
	printf("******************************\n");
	printf("**********1.开始游戏**********\n");
	printf("**********0.结束游戏**********\n");
	printf("******************************\n");
}

此函数负责打印菜单,目的在于使main函数中的思路更加直观。

?menu函数效果


? ? ? ? ?3.game函数

void game()
{
	char board[ROW][COL];
	char ret;
	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("玩家获胜\n");
	else if (ret == '#')
		printf("电脑获胜\n");
	else
		printf("平局\n");
}

此函数同样是为了使main函数中的思路更加直观,将整个三子棋游戏逻辑单独显示。

由于棋盘是二维的,因此需要创建一个二维数组并完全初始化。初始化后则需要将棋盘打印出来展示给玩家,此后需要通过循环时间玩家和电脑反复下棋并判断游戏是否结束。


? ? 源文件game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;
	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, j;
	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 PlayerMove(char board[ROW][COL], int row, int col)
{
	while (1)
	{
		int x,y;
		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;
			}
			else
				printf("输入坐标已被占用,请重新输入\n");
		}
		else
			printf("输入坐标非法,请重新输入\n");
	}
}

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑走\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

char IsWin(char board[ROW][COL], int row, int col)
{
	int x, y;
	for (x = 0; x < row - 2; x++)
	{
		for (y = 0; y < col; y++)//判断一列
		{
			if (board[x][y] == board[x + 1][y] && board[x + 1][y] == board[x + 2][y] && board[x][y] != ' ')
				return board[x][y];
		}
		for (y = 0;y < col - 2; y++)//判断右斜
		{
			if (board[x][y] == board[x + 1][y + 1] && board[x + 1][y + 1] == board[x + 2][y + 2] && board[x][y] != ' ')
				return board[x][y];
		}
		for (y = 3; y < col; y++)//判断左斜
		{
			if (board[x][y] == board[x + 1][y - 1] && board[x + 1][y - 1] == board[x + 2][y - 2] && board[x][y] != ' ')
				return board[x][y];
		}
	}
	for (x = 0; x < row; x++)
	{
		for (y = 0; y < col - 2; y++)//判断一列
		{
			if (board[x][y] == board[x][y + 1] && board[x][y + 1] == board[x][y + 2] && board[x][y] != ' ')
				return board[x][y];
		}
	}
	for (x = 0; x < row; x++)
	{
		for (y = 0; y < col; y++)//判断满格
		{
			if (board[x][y] == ' ')
				return 'C';
		}
	}
	return 'P';
}

? ? ? ? 1.InitBoard函数

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

?InitBoard函数通过两个for循环将二维数组全部初始化为' '。


? ? ? ? 2.DisplayBoard函数

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i, j;
	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");
	}

?DisplayBoard函数同样是通过双重for循环打印出二维数组以及棋盘的分界线。

DisplayBoard函数效果


? ? ? ? 3.PlayerMove函数

void PlayerMove(char board[ROW][COL], int row, int col)
{
	while (1)
	{
		int x,y;
		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;
			}
			else
				printf("输入坐标已被占用,请重新输入\n");
		}
		else
			printf("输入坐标非法,请重新输入\n");
	}
}

当玩家下棋时,需要玩家成功完成下棋步骤才能够执行下一步,因此需要一个while(1)死循环使得当玩家输入坐标非法或被占用时能够重新下棋。

PlayerMove函数中需要注意二维数组的行和列都是从0开始,因此需要将玩家输入的坐标各减去1才能对应上。

?PlayerMove函数效果


? ? ? ? 4.ComputerMove函数

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑走\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

为防止电脑随机下棋位置非法,使用rand()% row和?rand()% col限定随机数的只能取0到(row-1)或(col-1)。

同时为了防止电脑随机下棋位置已被占用后可以重新下棋,同样需要一个while(1)死循环和if语句判断并break。

?ComputerMove函数效果


? ? ? ? ?5.IsWin函数

char IsWin(char board[ROW][COL], int row, int col)
{
	int x, y;
	for (x = 0; x < row - 2; x++)
	{
		for (y = 0; y < col; y++)//判断一列
		{
			if (board[x][y] == board[x + 1][y] && board[x + 1][y] == board[x + 2][y] && board[x][y] != ' ')
				return board[x][y];
		}
		for (y = 0;y < col - 2; y++)//判断右斜
		{
			if (board[x][y] == board[x + 1][y + 1] && board[x + 1][y + 1] == board[x + 2][y + 2] && board[x][y] != ' ')
				return board[x][y];
		}
		for (y = 3; y < col; y++)//判断左斜
		{
			if (board[x][y] == board[x + 1][y - 1] && board[x + 1][y - 1] == board[x + 2][y - 2] && board[x][y] != ' ')
				return board[x][y];
		}
	}
	for (x = 0; x < row; x++)
	{
		for (y = 0; y < col - 2; y++)//判断一列
		{
			if (board[x][y] == board[x][y + 1] && board[x][y + 1] == board[x][y + 2] && board[x][y] != ' ')
				return board[x][y];
		}
	}
	for (x = 0; x < row; x++)
	{
		for (y = 0; y < col; y++)//判断满格
		{
			if (board[x][y] == ' ')
				return 'C';
		}
	}
	return 'P';
}

因为涉及到二维数组各个量,因此每次判断都需要双重for循环。

需要注意的是无论是判断一行、一列、左斜还是右斜,都需要限制x和y的范围,防止超出二维数组的范围。

另外还得确保对于是否满格平局的判断放在最后,即使判断满格和判断一列外层的for函数一致也不能放到一起,否则前四种情况还没完成判断时就返回了'C',导致即使玩家或电脑赢了,游戏依旧继续。

最后通过返回值判断电脑赢了、玩家赢了、平局还是继续。


结语

目前该函数还有优化的空间,尤其是对电脑下棋的算法进行设计,但目前我还不具备足够的能力去设计,日后也许会填坑。

第一次写博客的感觉很奇妙,本来是已经想好了大致的思路,但一上手后还是有点混乱。经过反复的打磨,我也对自己写的代码有了更深刻的认识,也许几天后就忘记的代码一下子就印在了脑海里,这让我更加确信写博客并不是浪费时间,而是对知识点的巩固,与此同时还能收获大家得指正,何乐而不为呢?

在这之后我也会利用闲暇时间将自己写过的代码以及学习经历写成博客分享出来,预计下一篇是这几天刚写完的扫雷游戏。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-20 15:36:17  更:2021-09-20 15:38:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:59:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码