C语言实现三子棋、五子棋、扫雷游戏合集详解

游戏大厅实现功能(代码部分高亮显示)

  • 显示游戏菜单,并提示玩家选择游戏项目(该功能放入main函数中):

1.进入三子棋游戏
2.进入五子棋游戏
3.进入扫雷游戏
4.退出游戏大厅

  • 本项目通过多文件实现,每个游戏分别由一个源文件和头文件组成,再加上main源文件:
    在这里插入图片描述
  • 游戏主函数main.c
// 通过下列三行代码提前声明头文件
#include "ThreeChess.h"
#include "Gobang.h"
#include "MineClearance.h"

void Menu()
{
    
    
	printf("+-----------------------+\n");
	printf("|       1.三子棋        |\n");
	printf("|       2.五子棋        |\n");
	printf("|       3.扫雷游戏      |\n");
	printf("|       0.Exit !        |\n");
	printf("+-----------------------+\n");
}

int main()
{
    
    
	int select = 0;
	int quit = 0;
	while (!quit)
	{
    
    
		printf("请输入游戏选项:\n");
		Menu();
		scanf("%d", &select);
		printf("\n");
		switch (select)
		{
    
    
		case 1:
			ThreeChess();
			break;
		case 2:
			Gobang();
			break;
		case 3:
			MineClearance();
			break;
		case 0:
			quit = 1;
			break;
		default:
			printf("输入选项错误!请重新选择\n");
			break;
		}
	}
	printf("再见!");

	system("pause");
	return 0;
}

三子棋模块

1.游戏介绍

  • 本程序实现了三子棋人机对战(玩家执 X 棋,电脑执 O 棋)
  • 本程序电脑落子仅为随机落子,智能难度暂未实现
  • 三子棋人机对战,玩家先走
  • 通过system("cls"); //刷新界面实现刷新界面功能,更新游戏界面

2.棋盘储存及初始化

  • 通过二维数组char board[ROW][COL]存储信息
#define ROW 3      //三子棋行数
#define COL 3      //三子棋列数

char board_1[ROW][COL];
  • 初始化函数:
    棋盘启示为玩家展示空白,通过空格实现;
    将空格宏定义,方便后期修改;
#define INIT ' '   //初始化空格

static void InitBoard(char board[][COL]) //初始化棋盘
{
    
    
	for (int i = 0; i < ROW; i++)
	{
    
    
		for (int j = 0; j < COL; j++)
		{
    
    
			board[i][j] = INIT;
		}
	}
}

3.展示棋盘

static void ShowBoard(char board[][COL])  //显示棋盘
{
    
    
	system("cls");  //刷新界面
	printf("  ");
	for (int i = 0; i < COL; i++)
	{
    
    
		printf(" %2d ", i+1);
	}
	printf("\n  +-----------+\n");
	for (int i = 0; i < ROW; i++)
	{
    
    
		printf("%2d", i+1);
		for (int j = 0; j < COL; j++)
		{
    
    
			printf("| %c ", board[i][j]);
		}
		printf("|");
		printf("\n  +-----------+\n");
	}
}

游戏开始时第一次展示棋盘:
在这里插入图片描述

4.玩家落子

  • 三子棋人机对战,玩家先走;
  • 初始化完成后玩家进行落子,具体操作是输入棋盘的坐标,系统为其对应位置显示X代表玩家落子成功;

5.电脑落子

  • 通过 rand 随机函数操作,使其在棋盘范围内随机显示一个 O
srand((unsigned long)time(NULL)); //生成随机数种子,为电脑操作函数做准备 
   	int x = rand() % ROW;  //电脑的输入坐标是通过随机函数生成
   	int y = rand() % COL;

6.检查判断(核心)

  • 玩家或者电脑每次落子后,都需要判断一次游戏输赢情况,以此来决定是否游戏结束或继续
  • 一局游戏有四种状态,赢、输、平局、继续
    通过对棋盘的每一行每一列以及每一条对角线判断是否全是同种棋子,进而得到本局游戏的状态

7.总体代码

  • 三子棋头文件ThreeChess.h
#pragma once

#include<stdio.h>
#include<Windows.h>
#pragma warning(disable:4996)

#include <time.h>
#include <stdlib.h>
#define ROW 3      //三子棋行数
#define COL 3      //三子棋列数
#define NEXT 'N'   //继续
#define DRAW 'W'   //平局
#define BLACK 'X'  //用户下黑棋 X
#define WHITE 'O'  //电脑下白棋 O
#define INIT ' '   //初始化空格


extern void ThreeChess();
  • 三子棋源文件ThreeChess.c
#include "ThreeChess.h"

static void InitBoard(char board[][COL]) //初始化棋盘
{
    
    
	for (int i = 0; i < ROW; i++)
	{
    
    
		for (int j = 0; j < COL; j++)
		{
    
    
			board[i][j] = INIT;
		}
	}
}

static void ShowBoard(char board[][COL])  //显示棋盘
{
    
    
	system("cls");  //刷新界面
	printf("  ");
	for (int i = 0; i < COL; i++)
	{
    
    
		printf(" %2d ", i+1);
	}
	printf("\n  +-----------+\n");
	for (int i = 0; i < ROW; i++)
	{
    
    
		printf("%2d", i+1);
		for (int j = 0; j < COL; j++)
		{
    
    
			printf("| %c ", board[i][j]);
		}
		printf("|");
		printf("\n  +-----------+\n");
	}
}

static void UserMove(char board[][COL])  //用户操作
{
    
    
	int x = 0,y = 0;
	while (1)
	{
    
    
		printf("请输入你的落子坐标<x,y>:");
		scanf("%d %d", &x, &y);
		if (x<1 || y<1 || x>COL || y>ROW)
		{
    
    
			printf("输入坐标超出范围!请重新输入");
			continue;
		}
		if (board[x-1][y-1] == INIT)
		{
    
    
			board[x-1][y-1] = BLACK;
			break;
		}
		else
		{
    
    
			printf("输入的坐标已有落子!请重新输入");
		}
	}
}

static void PCMove(char board[][COL])  //电脑操作
{
    
    
	while (1)
	{
    
    
		int x = rand() % ROW;  //电脑的输入坐标是通过随机函数生成
		int y = rand() % COL;

        if (board[x][y] == INIT)
		{
    
    
			board[x][y] = WHITE;
			break;
		}
	}
}

static char Judge(char board[][COL])
{
    
    
	for (int i = 0; i < ROW; i++)  //判断三行是否有满足三子成线
	{
    
    
		if (board[i][0] == board[i][1] && \
			board[i][1] == board[i][2] && \
			board[i][0] != INIT)
		{
    
    
			return board[i][0];
		}
	}
	for (int j = 0; j < COL; j++)  //判断三列是否有满足三子成线
	{
    
    
		if (board[0][j] == board[1][j] && \
			board[1][j] == board[2][j] && \
			board[0][j] != INIT)
		{
    
    
			return board[0][j];
		}
	}
	//判断右斜方向
	if (board[0][0] == board[1][1] && \
		board[1][1] == board[2][2] && \
		board[1][1] != INIT)
	{
    
    
		return board[1][1];
	}
	//判断左斜方向
	if (board[0][2] == board[1][1] && \
		board[1][1] == board[2][0] && \
		board[1][1] != INIT)
		{
    
    
			return board[1][1];
		}
	for (int i = 0; i < ROW; i++)  //若棋盘未下满继续
	{
    
    
		for (int j = 0; j < COL; j++)
		{
    
    
			if (board[i][j] == INIT)
			{
    
    
				return NEXT;
			}
		}
	}
	return DRAW;  //棋盘已满但未有输赢结果
}

void ThreeChess() //人机对战 用户先走
{
    
    
	srand((unsigned long)time(NULL)); //生成随机数种子,为电脑操作函数做准备

	char board_1[ROW][COL];
	InitBoard(board_1);
	char result = 0;
	while (1)
	{
    
    
		ShowBoard(board_1);
		UserMove(board_1);
		result = Judge(board_1);
		if (result != NEXT)
		{
    
    
			break;
		}
		ShowBoard(board_1);
		PCMove(board_1);
		result = Judge(board_1);
		if (result != NEXT)
		{
    
    
			break;
		}
	}
	ShowBoard(board_1);
	switch (result)
	{
    
    
	case BLACK:
		printf("你赢了!\n");
		break;
	case WHITE:
		printf("你输了!\n");
		break;
	case DRAW:
		printf("平局!\n");
		break;
	default:
		printf("bug!\n");
		break;
	}
}

五子棋模块

1.游戏介绍

  • 本程序实现了五子棋双人对战(玩家1执 ● 棋,玩家2执 ○ 棋)
  • 暂不能实现人机对战(人机对战需人工智能,三子棋的随机落子在五子棋中不可取)
  • 通过system("cls"); //刷新界面实现刷新界面功能,更新游戏界面

2.棋盘储存及初始化

  • 储存
    1)与三子棋类似,我们通过一个二维数组来构建数据结构;
    2)不过五子棋的棋盘与三子棋还是有所不同,五子棋的棋盘可以无限大,这里可以通过宏定义来决定棋盘大小
    3)二维数组类型为int,方便初始化;
#define ROW_G 10    //五子棋棋盘行数
#define COL_G 10    //五子棋棋盘列数

int board[ROW_G][COL_G] = {
    
     0 };  //顺便进行了初始化
  • 初始化:由于二维数组类型为int,所以直接初始化为 0 即可
    (注意:这里初始化为 0 并不意味着棋盘展示给玩家的空棋盘为 0 ;在后续 展示棋盘函数 中会做详细约数)

3.展示棋盘

static void ShowBoard(int board[][COL_G])  //展示
{
    
    
	system("cls");  //刷新界面
	printf("  ");
	for (int i = 0; i < COL_G; i++)
	{
    
    
		printf(" %3d ", i);
	}
	printf("\n  +--------------------------------------------------+\n");
	for (int i = 0; i < ROW_G; i++)
	{
    
    
		printf("%2d|", i);
		for (int j = 0; j < COL_G; j++)
		{
    
    
			if (board[i][j] == 0)
			{
    
    
				printf(" ﹡  ");  //空棋盘时
			}
			else if (board[i][j] == USER1)
			{
    
    
				printf(" ●  ");  //玩家一落子显示
			}
			else if (board[i][j] == USER2)
			{
    
    
				printf(" ○  ");  //玩家二落子显示
			}
		}
		printf("|\n");
	}
	printf("  +--------------------------------------------------+\n");
}

游戏开始时第一次展示棋盘:
在这里插入图片描述

4.玩家落子

  • 玩家通过输入棋盘坐标进行落子,ShowBoard函数通过判断玩家1、2的宏定义来决定显示什么棋;
#define USER1 1    //用户1标志
#define USER2 2    //用户2标志
  • 玩家落子的合法性判断
    1)判断玩家是否落子于棋盘之中
    2)判断玩家输入坐标是否已有落子
static void PlayMove(int board[][COL_G], int who)  //玩家操作
{
    
    
	while (1){
    
    
		printf("Please Enter Your Pos<x,y>[player%d]# ", who);
		scanf("%d %d", &x, &y);  //这里将玩家输入的坐标放进全局变量中,方便Judge函数
		if (x < 0 || x > ROW_G - 1 || y < 0 || y > COL_G - 1){
    
    
			printf("this postion is error!\n");
			continue;
		}
		if (board[x][y] == 0){
    
    
			board[x][y] = who; //who意义详见下方注释
			break;
		}
		else{
    
    
			printf("this postion is not empty!\n");
			continue;
		}
	}
}

【注】who的意义
通过who形参和USER1、USER2的宏定义可以使同一个落子函数实现不同玩家的落子

5.检查判断(核心)

  • 落完一子后结果有四种:玩家1赢,玩家2赢,平局和继续
  • 由五子棋玩法可知,需要判断某子是否五子连珠时,另一个棋子也就是上一个玩家必定没有五子连珠
  • 判断某子是否五子连珠,只需统计该棋子上下左右和斜方向共8个方向连续相同棋子数
  • 判断是否平局通过棋盘是否还有空位

6.总体代码

  • 五子棋头文件ThreeChess.h
#pragma once

#include<stdio.h>
#include<Windows.h>
#pragma warning(disable:4996)

#define ROW_G 10    //五子棋棋盘行数
#define COL_G 10    //五子棋棋盘列数
#define USER1 1    //用户1标志
#define USER2 2    //用户2标志
#define NEXT_G 3   //继续
#define DRAW_G 4   //平局

#define UP 10
#define RIGHT_UP 11
#define RIGHT  12
#define RIGHT_DOWN 13
#define DOWN 14
#define LEFT_DOWN 15
#define LEFT 16
#define LEFT_UP 17

extern void Gobang();
  • 五子棋源文件ThreeChess.c
#include "Gobang.h"

int x = 0, y = 0;

static void ShowBoard(int board[][COL_G])
{
    
    
	system("cls");  //刷新界面
	printf("  ");
	for (int i = 0; i < COL_G; i++)
	{
    
    
		printf(" %3d ", i);
	}
	printf("\n  +--------------------------------------------------+\n");
	for (int i = 0; i < ROW_G; i++)
	{
    
    
		printf("%2d|", i);
		for (int j = 0; j < COL_G; j++)
		{
    
    
			if (board[i][j] == 0)
			{
    
    
				printf(" ﹡  ");
			}
			else if (board[i][j] == USER1)
			{
    
    
				printf(" ●  ");
			}
			else if (board[i][j] == USER2)
			{
    
    
				printf(" ○  ");
			}
		}
		printf("|\n");
	}
	printf("  +--------------------------------------------------+\n");
}

static void PlayMove(int board[][COL_G], int who)  //玩家操作
{
    
    
	while (1){
    
    
		printf("Please Enter Your Pos<x,y>[player%d]# ", who);
		scanf("%d %d", &x, &y);  //这里将玩家输入的坐标放进全局变量中,方便Judge函数
		if (x < 0 || x > ROW_G - 1 || y < 0 || y > COL_G - 1){
    
    
			printf("this postion is error!\n");
			continue;
		}
		if (board[x][y] == 0){
    
    
			board[x][y] = who;
			break;
		}
		else{
    
    
			printf("this postion is not empty!\n");
			continue;
		}
	}
}

static int ChessCount(int board[][COL_G], int dir)  //对某一棋子周边计数,用在Judge函数
{
    
    
	int _x = x;  //设置局部变量拷贝,防止改变原始值
	int _y = y;
	int count = 0;
	//状态机
	while (1){
    
    
		switch (dir){
    
    
		case UP:
			_x--;
			break;
		case RIGHT_UP:
			_y++, _x--;
			break;
		case RIGHT:
			_y++;
			break;
		case RIGHT_DOWN:
			_x++, _y++;
			break;
		case DOWN:
			_x++;
			break;
		case LEFT_DOWN:
			_x++, _y--;
			break;
		case LEFT:
			_y--;
			break;
		case LEFT_UP:
			_x--, _y--;
			break;
		default:
			printf("ChessCount bug!\n");
			break;
		}
		if (_x < 0 || _x > ROW_G - 1 || _y < 0 || _y > COL_G - 1){
    
    
			break;
		}
		else{
    
    
			if (board[x][y] != 0 && board[x][y] == board[_x][_y]){
    
    
				count++;
			}
			else{
    
    
				break;
			}
		}
	}
	return count;
}

static char JudgeGoband(int board[][COL_G])
{
    
    
	if (ChessCount(board, UP) + ChessCount(board, DOWN) >= 4 || \
		//统计上方和下方棋子数量,结果为竖直方向的同类棋子数
		ChessCount(board, RIGHT_UP) + ChessCount(board, LEFT_DOWN) >= 4 || \
		//统计右上方和左下方棋子数量,结果为右斜方向的同类棋子数
		ChessCount(board, RIGHT) + ChessCount(board, LEFT) >= 4 || \
		//统计右方和左方棋子数量,结果为水平方向的同类棋子数
		ChessCount(board, RIGHT_DOWN) + ChessCount(board, LEFT_UP) >= 4
		//统计右下和左上方棋子数量,结果为左斜方向的同类棋子数
		)
	{
    
    
		return board[x][y];
	}
	for (int i = 0; i < ROW_G; i++)  //判断是否平局
	{
    
    
		for (int j = 0; j < COL_G; j++)
		{
    
    
			if (board[i][j] == 0)
			{
    
    
				return NEXT_G;
			}
		}
	}
	return DRAW_G;
}

void Gobang()  //只能实现两用户对战
{
    
    
	int board[ROW_G][COL_G] = {
    
     0 };
	char flag = 0;
	int curr = USER1;
	while (1)
	{
    
    
		ShowBoard(board);
		PlayMove(board, curr);
		flag = JudgeGoband(board);
		if (flag != NEXT_G)
		{
    
    
			break;
		}
		curr = (curr == USER1 ? USER2 : USER1);
	}
	ShowBoard(board);
	switch (flag){
    
    
	case USER1:
		printf("Player 1 win!\n");
		break;
	case USER2:
		printf("Player 2 win!\n");
		break;
	case DRAW_G:
		printf("DRAW!\n");
		break;
	default:
		printf("bug!\n");
		break;
	}

}

扫雷模块

1.游戏介绍

  • 本程序实现了扫雷小游戏
  • 系统可以随机埋雷,游戏难度通过宏定义控制雷的个数
  • 玩法:玩家输入一个棋盘坐标,若不是雷点,则在该坐标显示周围雷点个数;若是雷点,则游戏结束,展示所以雷点
  • 通过system("cls"); //刷新界面实现刷新界面功能,更新游戏界面

2.棋盘储存及初始化(重点)

  • 储存
    1)扫雷棋盘总体也是通过二维数组实现
    2)棋盘的数据结构可以影响到代码算法的难度等等。在这里我们初始化两个一样的棋盘,一个用来存放雷点,另一个是给玩家展示的棋盘
    3)棋盘大小可以通过宏定义自由控制,但是,我们棋盘大小的设置需要比展示给用户的棋盘大一圈
    原因:这是为了方便显示周围雷点个数,统计雷点个数即数该点周围的雷点数,当该点在棋盘四边或四角时,为了统计函数的方便,我们将实际棋盘扩大一圈,这样无论玩家输入的是那个点,我们都可用一个同样的函数处理
    在这里插入图片描述
#define ROW 8  //界面行  行与列都比用户显示的大一个,便于Judge函数
#define COL 8  //界面列

//初始化两个相同表格,一个对玩家显示,一个随机生成答案
char show_board[ROW][COL];  
char mine_board[ROW][COL];
  • 初始化
    一种对字符型数组初始化的简便操作,等价于三子棋的初始化
//借用memset函数对二进制数组初始化
memset(show_board, FUN, sizeof(show_board));  //玩家显示表格显示:?
memset(mine_board, '0', sizeof(mine_board));  //答案表格初始化为0

memset函数用法的讲解后期更新

3.展示棋盘

static void ShowLine(int col)  //使Showboard函数能够根据宏定义自适应大小
{
    
    
	printf("   +");
	for (int i = 0; i < (col - 2); i++){
    
    
		printf("----");
	}
	printf("+\n");
}

static void ShowBoard(char board[][COL])
{
    
    
	printf("     ");
	for (int i = 1; i <= (COL - 2); i++)
	{
    
    
		printf("%d   ", i);
	}
	printf("\n");
	ShowLine(COL);
	for (int i = 1; i <= (ROW - 2); i++)
	{
    
    
		printf("%-3d|", i);
		for (int j = 1; j <= (COL - 2); j++)
		{
    
    
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		ShowLine(COL);
	}
}

在这里插入图片描述

4.电脑布雷

通过随机函数rand布雷:

srand((unsigned long)time(NULL)); //生成随机数种子,为电脑操作函数做准备

int x = rand() % (ROW - 2) + 1; //下标范围[1 COL-2]
int y = rand() % (COL - 2) + 1;

5.返回周边雷数

static char CountMines(char board[][COL], int x, int y)  //返回周边炸弹个数
{
    
    
//求出周围雷的个数,转换成字符表示
	return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + \
		board[x][y + 1] + board[x + 1][y + 1] + board[x + 1][y] + \
		board[x + 1][y - 1] + board[x][y - 1] - 7 * '0';
}

6.总体代码

  • 扫雷头文件ThreeChess.h
#pragma once

#include<stdio.h>
#include<Windows.h>
#include <stdlib.h>
#include <time.h>
#pragma warning(disable:4996)

#define ROW 8  //界面行  行与列都比用户显示的大一个,便于Judge函数
#define COL 8  //界面列
#define FUN '?'  //show_board 显示
#define NUM 10  //炸弹数量

extern void MineClearance();
  • 扫雷源文件ThreeChess.c
#include"MineClearance.h"

static void SetMine(char board[][COL])
{
    
    
	int count = NUM;
	while (count)
	{
    
    
		int x = rand() % (ROW - 2) + 1; //下标范围[1 COL-2]
		int y = rand() % (COL - 2) + 1;
		if (board[x][y] == '0')
		{
    
    
			board[x][y] = '1';
			count--;
		}
	}
}

static void ShowLine(int col)  //使Showboard函数能够根据宏定义自适应大小
{
    
    
	printf("   +");
	for (int i = 0; i < (col - 2); i++){
    
    
		printf("----");
	}
	printf("+\n");
}

static void ShowBoard(char board[][COL])
{
    
    
	printf("     ");
	for (int i = 1; i <= (COL - 2); i++)
	{
    
    
		printf("%d   ", i);
	}
	printf("\n");
	ShowLine(COL);
	for (int i = 1; i <= (ROW - 2); i++)
	{
    
    
		printf("%-3d|", i);
		for (int j = 1; j <= (COL - 2); j++)
		{
    
    
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		ShowLine(COL);
	}
}

static char CountMines(char board[][COL], int x, int y)  //返回周边炸弹个数
{
    
    
	return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + \
		board[x][y + 1] + board[x + 1][y + 1] + board[x + 1][y] + \
		board[x + 1][y - 1] + board[x][y - 1] - 7 * '0';
}

void MineClearance()
{
    
    
	srand((unsigned long)time(NULL));  //随机数种子
	//初始化两个相同表格,一个对玩家显示,一个随机生成答案
	char show_board[ROW][COL];  
	char mine_board[ROW][COL];
	//借用memset函数对二进制数组初始化
	memset(show_board, FUN, sizeof(show_board));  //玩家显示表格显示:?
	memset(mine_board, '0', sizeof(mine_board));  //答案表格初始化为0

	SetMine(mine_board);  //随机在答案表格生成炸弹坐标

	int count = (ROW - 2)*(COL - 2) - NUM;
	while (count)
	{
    
    
		system("cls");
		ShowBoard(show_board);
		printf("Please Enter Your Postion<x,y>: ");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x < 1 || x > COL || y < 1 || y > ROW)
		{
    
    
			printf("Postion Error!\n");
			continue;
		}
		if (show_board[x][y] != FUN)
		{
    
    
			printf("Postion Is not *\n");
			continue;
		}
		if (mine_board[x][y] == '1')
		{
    
    
			printf("game over!\n");
			ShowBoard(mine_board);
			break;
		}
		show_board[x][y] = CountMines(mine_board, x, y);  //提示功能函数
		count--;
	}
}

猜你喜欢

转载自blog.csdn.net/Sober_harmonic/article/details/118014648