主页:醋溜马桶圈-CSDN博客
目录
1.C语言实现三子棋
1.1 整体思路
我们需要有三个文件:
- test.c —— 测试游戏
- game.h —— 游戏函数的声明
- game.c —— 游戏函数的实现
1.2 游戏菜单的创建
进入游戏时,首先出现的是菜单界面
我们定义一个“菜单函数”menu( )来实现这一功能
游戏菜单应该实现这几个功能:
- 游戏的进入
- 游戏的退出
- 非法输入的返回提示和说明
接下来是我们实现这一功能的代码块
#include<stdio.h>
#include"game.h"
void menu() {
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
int main() {
int input = 0;
do
{
menu();
printf("请输入:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
这是该模块菜单函数的运行截图
1.3 游戏主体的实现
1.3.1 棋盘的初始化
游戏刚开始时,我们需要对棋盘进行初始化操作,对棋盘进行清空,以保证后续游戏的正常进行。我们定义一个InitBoard()函数来进行棋盘的初始化操作
代码如下
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
1.3.2 打印棋盘
初始化棋盘后就需要打印棋盘了
打印棋盘的代码如下
void DisplayBoard(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++)
{
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");
}
}
1.3.3 玩家下棋
打印好棋盘,就要进行我们的下棋阶段
我们规定玩家先下棋
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] != ' ')
{
printf("该位置已被占用,请输入其他坐标\n");
}
else
{
board[x - 1][y - 1] = '*';
break;
}
}
else
{
printf("输入非法,请重新输入\n");
}
}
}
玩家输入对应的合法坐标下棋,接下来就是电脑下棋
暂且不设计电脑对抗玩家的算法,就让电脑产生一个棋盘内部的随机坐标
1.3.4 电脑下棋
void ComputerBoard(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()函数,但这个函数需要srand()设置,所以我们在main()函数开头加上srand()
接下来写一个判断输赢的函数来进行判断
1.3.5 判断输赢
三子棋游戏出现的结果有以下四种,我们用各自对应的符号进行判断
//玩家赢 - *
//电脑赢 - #
//平局 - Q
//继续游戏 - C
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 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[0][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];
if (IsFull(board, row, col))
return 'Q';
return 'C';
}
1.3.6 棋盘满
接下来我们还需要有一个函数来判断棋盘是否已满
static 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;
}
我们用static修饰这个函数,使当前函数只能在本文件内部使用
1.3.7 game()函数
上面我们铺垫了所有三子棋所要用到的函数,这些函数在game()函数中使用
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;
//电脑下棋
ComputerBoard(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
{
printf("玩家赢\n");
}
if (ret == '#')
{
printf("电脑赢\n");
}
if (ret == 'Q')
{
printf("平局\n");
}
}
1.4 整体代码
这些代码我们在实际使用时是需要封装在不同的文件内的
所以我们定义了test.c,game.c,game.h文件封装我们所有用到的函数以及头文件
1.4.1 test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
void menu() {
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
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;
//电脑下棋
ComputerBoard(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
{
printf("玩家赢\n");
}
if (ret == '#')
{
printf("电脑赢\n");
}
if (ret == 'Q')
{
printf("平局\n");
}
}
int main() {
int input = 0;
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);
return 0;
}
1.4.2 game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
void InitBoard(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++)
{
board[i][j] = ' ';
}
}
}
void DisplayBoard(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++)
{
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)
{
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] != ' ')
{
printf("该位置已被占用,请输入其他坐标\n");
}
else
{
board[x - 1][y - 1] = '*';
break;
}
}
else
{
printf("输入非法,请重新输入\n");
}
}
}
void ComputerBoard(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;
}
}
}
static 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;
int j = 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[0][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];
if (IsFull(board, row, col))
return 'Q';
return 'C';
}
1.4.3 game.h
#pragma once
#define ROW 3
#define COL 3
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//初始化棋盘
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 ComputerBoard(char board[ROW][COL], int row, int col);
//判断输赢
//有四种可能
//玩家赢 - *
//电脑赢 - #
//平局 - Q
//继续游戏 - C
char IsWin(char board[ROW][COL], int row, int col);
我们把所有的函数声明和头文件以及宏定义都放在我们自己创建的game.h文件中
我们在其他文件中使用时只需要包含该文件用双引号引住即可
2.C语言实现扫雷游戏
2.1 整体思路
跟三子棋一样,我们首先创建三个文件:game.h , game.c , test.c
- 1.用define定义常量,定义数组的行和列,这样改变数组行列不需要整个修改代码,只需要改变define后面定义常量的值;
- 2.需要一个菜单来供我们选择进入游戏还是退出游戏,menu函数;
- 3.玩游戏肯定不止玩一局,所以需要设置循环do...while;
- 4.需要俩个数组,一个用来设置雷(mine),存放雷的信息;一个用来让玩家排查雷(show),存放玩家输入的坐标信息;
- 5.需要初始化俩个数组,我们把要埋雷的数组初始化为’0‘,要排雷的数组初始化为’*‘;
- 6.需要打印棋盘让玩家来排雷;
- 7.需要设置雷和排雷函数;
2.2 游戏主体的实现
2.2.1 打印菜单
定义一个menu()函数来打印菜单
void menu() {
printf("******************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************\n");
}
2.2.2 玩家选择
我们在main()中采取do-while循环来实现重复游戏,用switch()来获取玩家的选择
int main() {
int input = 0;
do {
menu();
printf("请选择>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
目前我们的实现效果是这样的
2.2.3 扫雷的逻辑
2.2.3.1 布置雷的逻辑
假设我们实现一个9*9的棋盘,上面布置十个雷
这样我们就需要一个9*9的数组来存放雷
假设这是我们的棋盘
最开始每个位置都放0;0表示非雷
当我们布置雷的时候,把雷放成1
2.2.3.2 排雷的第一种情况
假设我们在排这个绿色坐标的时候,它不是雷,这时候我们需要统计它周围红圈中8个坐标的雷的个数;像上面这种情况,我们需要显示1;但是这个1会与我们表示雷的1冲突,为了解决这个问题,我们再单独给他一个数组,全部放成 ‘ * ’ ;
这个时候,我们在 ‘ * ’ 棋盘中,对应的位置显示‘1’表示这个坐标周围的八个坐标里有一个雷;我们给玩家展示的时候,展示第2个数组
- 第一个棋盘存雷和非雷的信息
- 第二个棋盘存玩家排查出来的雷的信息
2.2.3.3 排雷的第二种情况
假设我们要排查边缘的这个坐标,这个是超出了我们9*9的数组范围
这个时候为了防止越界,我们再把这个数组扩充,即在设置存雷的数组的时候,范围设置成11*11
像这样
这个时候,就不存在越界行为了
为了便于计算,我们的展示棋盘同样扩大到11*11(严丝合缝);
2.2.4 棋盘的初始化
我们定一个InitBoard()函数来初始化棋盘
定义两个11*11的数组
这个数组用来放布置好的雷的信息(11*11)
这个数组用来放排查出的雷的信息(11*11)
为了保证两个数组严格的统一,包括元素类型也要统一,我们定义为char数组
game.h
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
game.c
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
board [i][j] = set;
}
}
}
test.c
void game() {
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
}
2.2.5 打印棋盘
我们定义一个DisplayBoard()函数来打印棋盘
在打印的时候,我们可以打印出坐标
2.2.5.1 代码
game.c
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
int i = 0, j = 0;
for (i = 0; i <= col; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
for (j = 1; j <= col; j++){
printf("%c ", board[i][j]);
}
printf("\n");
}
}
test.c
void game() {
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
}
2.2.5.2 打印效果
2.2.6 布置雷
我们定义一个setMine()函数来布置雷
假设我要布置十个雷
- 随机布置
- 用1表示雷
这里我们又要用到我们的随机数函数rand(),使用rand函数必然需要在main()函数中调用srand();
//布置雷
void setMine(char mine[ROWS][COLS], int row, int col) {
int count = EASY_COUNT;
while (count) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0') {
mine[x][y] = '1';
count--;
}
}
}
2.2.7 排雷
排查雷的逻辑是:
- 如果这个位置不是雷,就计算这个位置周围八个坐标有几个雷,并显示雷的个数
- 如果这个位置是雷,就炸死了,游戏结束
- 如果把不是雷的位置都找出来了,那么游戏也结束,玩家胜利
在扫雷的过程中,我们还需要计算周围雷的个数
定义一个GetMineCount()函数来统计周围雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
return(mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0');
}
定义一个FindMine()函数来排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT) {
printf("请输入要排查雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (show[x][y] == '*') {
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(show, ROW, COL);
break;
}
else {
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else {
printf("该坐标已经被排查了\n");
}
}
else {
printf("非法坐标,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
2.2.8 game()函数
void game() {
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//布置雷
setMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
2.3 整体代码
2.3.1 game.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//十个雷
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布雷
void setMine(char mine[ROWS][COLS], int row, int col);
//扫雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
//计算扫出来的雷个数
int GetMineCount(char mine[ROWS][COLS], int x, int y);
2.3.2 game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
board [i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
int i = 0, j = 0;
printf("--------扫雷--------\n");
for (i = 0; i <= col; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
for (j = 1; j <= col; j++){
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------扫雷--------\n");
}
//布置雷
void setMine(char mine[ROWS][COLS], int row, int col) {
int count = EASY_COUNT;
while (count) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0') {
mine[x][y] = '1';
count--;
}
}
}
//扫雷
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
return(mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0');
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT) {
printf("请输入要排查雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (show[x][y] == '*') {
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(show, ROW, COL);
break;
}
else {
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else {
printf("该坐标已经被排查了\n");
}
}
else {
printf("非法坐标,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
2.3.3 test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu() {
printf("******************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************\n");
}
void game() {
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//布置雷
setMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main() {
srand((unsigned int)time(NULL));
int input = 0;
do {
menu();
printf("请选择>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}