xuanyee

导航

统计

软件创新开发第一周作业

有一个用c++编写的推箱子小游戏程序,但其中有许多需要优化改善的地方,本博客就基于此展开讨论以及修正。
原代码:#include <stdio.h>

include <stdlib.h>

include <conio.h>

int map[2][7][8] =
{
//0:空的 1:■ :墙
//3:☆ 4:★ //目的地和箱子
//5:※ //人
//7:⊙ //目的(3)和箱子(4)在一起
//8:※ //人(5)和目的(3)在一起
//为让多种情况使用一种算法

{
1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 3, 1, 0, 1, 1, 3, 1,
1, 4, 0, 0, 4, 0, 3, 1,
1, 0, 1, 0, 1, 1, 4, 1,
1, 0, 0, 5, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1
},
{
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 0, 0, 0, 0, 1,
	1, 3, 1, 0, 1, 1, 3, 1,
	1, 3, 4, 5, 4, 0, 3, 1,
	1, 4, 1, 0, 1, 1, 4, 1,
	1, 0, 0, 0, 0, 0, 0, 1,
	1, 1, 1, 1, 1, 1, 1, 1
}

};
int cas = 0; //为0表示第一关
//记录每一关的箱子数 或者是项目和目的在一起的总数
int boxSum[2] = {3,4};
//地图绘图
void drawMap()
{
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 8; j++)
{
if (j == 0)
printf("\t\t");
switch (map[cas][i][j])
{
// //0:空的 1:■ :墙
case 0:
printf(" ");
break;
case 1:
printf("■");
break;
//3:☆ 4:★ //目的地和箱子
case 3:
printf("☆");
break;
case 4:
printf("★");
break;
//5:※ //人
case 5:
case 8:
printf("※");
break;
case 7:
printf("⊙");
break;
//7:⊙ //目的(3)和箱子(4)在一起
//8:※ //人(5)和目的(3)在一起
}
}
printf("\n");
}
}
//按键处理
void keyDown()
{
//分析按键过程
//定位人在哪里
//人有两种情况:第一个是:人,第二个:人站在目的上
int i, j;
for (i = 0; i < 7; i++)
{
for (j = 0; j < 8; j++)
{
if (map[cas][i][j] == 5 || map[cas][i][j] == 8)
{
break;
}
}
if (map[cas][i][j] == 5 || map[cas][i][j] == 8)
{
break;
}
}

char ch = _getch();	//看不见的字符输入,+头文件 conio.h
switch (ch)
{
	//72 80   75 77
case 'w':
case 'W':
case 72:
	//下一个地方等于空地或者是目的 能走
	if (map[cas][i - 1][j] == 0 || map[cas][i - 1][j] == 3)
	{
		//3+5=8 :表示目的和人在一起
		//新地方(map[i-1][j])人(5)来了
		map[cas][i - 1][j] += 5;
		//老地方(map[i][j])人(5)走了
		map[cas][i][j] -= 5;
	}
	//如果下一个是箱子,要进一步判断能走
	//注意点:箱子两种状态:箱子,箱子和目的在一起
	else if (map[cas][i - 1][j] == 4 || map[cas][i - 1][j] == 7)
	{
		//做箱子的下一个地方判断能不能走
		if (map[cas][i - 2][j] == 0 || map[cas][i - 2][j] == 3)
		{
			//新的地方箱子来了
			map[cas][i - 2][j] += 4;
			//箱子的位置:箱子(-4)走了 人来(+5)
			map[cas][i - 1][j] += 1;
			//原来的地方人走了
			map[cas][i][j] -= 5;
		}

	}

	break;
case 's':
case 'S':
case 80:
	//下一个地方等于空地或者是目的 能走
	if (map[cas][i + 1][j] == 0 || map[cas][i + 1][j] == 3)
	{
		//3+5=8 :表示目的和人在一起
		//新地方(map[i-1][j])人(5)来了
		map[cas][i + 1][j] += 5;
		//老地方(map[i][j])人(5)走了
		map[cas][i][j] -= 5;
	}
	else if (map[cas][i + 1][j] == 4 || map[cas][i + 1][j] == 7)
	{
		//做箱子的下一个地方判断能不能走
		if (map[cas][i + 2][j] == 0 || map[cas][i + 2][j] == 3)
		{
			//新的地方箱子来了
			map[cas][i + 2][j] += 4;
			//箱子的位置:箱子(-4)走了 人来(+5)
			map[cas][i + 1][j] += 1;
			//原来的地方人走了
			map[cas][i][j] -= 5;
		}

	}
	break;

case 'a':
case 'A':
case 75:
	//下一个地方等于空地或者是目的 能走
	if (map[cas][i][j - 1] == 0 || map[cas][i][j - 1] == 3)
	{
		//3+5=8 :表示目的和人在一起
		//新地方(map[i-1][j])人(5)来了
		map[cas][i][j - 1] = map[cas][i][j - 1] + 5;
		//老地方(map[i][j])人(5)走了
		map[cas][i][j] = map[cas][i][j] - 5;
		//j+=5  j=j+5

	}
	else if (map[cas][i][j - 1] == 4 || map[cas][i][j - 1] == 7)
	{
		//做箱子的下一个地方判断能不能走
		if (map[cas][i][j - 2] == 0 || map[cas][i][j - 2] == 3)
		{
			//新的地方箱子来了
			map[cas][i][j - 2] += 4;
			//箱子的位置:箱子(-4)走了 人来(+5)
			map[cas][i][j - 1] += 1;
			//原来的地方人走了
			map[cas][i][j] -= 5;
		}

	}

	break;
case 'D':
case 'd':
case 77:
	//下一个地方等于空地或者是目的 能走
	if (map[cas][i][j + 1] == 0 || map[cas][i][j + 1] == 3)
	{
		//3+5=8 :表示目的和人在一起
		//新地方(map[i-1][j])人(5)来了
		map[cas][i][j + 1] += 5;
		//老地方(map[i][j])人(5)走了
		map[cas][i][j] -= 5;
	}
	
	//下一个地方是箱子,判断箱子的下一个地方是不是目的和空地
	else if (map[cas][i][j + 1] == 4 || map[cas][i][j + 1] == 7)
	{
		//做箱子的下一个地方判断能不能走
		if (map[cas][i][j + 2] == 0 || map[cas][i][j + 2] == 3)
		{
			//新的地方箱子来了
			map[cas][i][j + 2] += 4;
			//箱子的位置:箱子(-4)走了 人来(+5)
			map[cas][i][j + 1] += 1;
			//原来的地方人走了
			map[cas][i][j] -= 5;
		}

	}
	break;
}

}
//胜负判断
//用什么判断胜负: 箱子到达目的的个数
int gameOver()
{
int count = 0;
//所有的地方找一遍
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 8; j++)
{
if (map[cas][i][j] == 7)
count++;
}
}
return count;
}
//箱子数是零的时候也是胜利
int gameOver2()
{
int count = 3;
//所有的地方找一遍
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 8; j++)
{
if (map[cas][i][j] == 3)
count--;
}
}
return count;
}

int main()
{
while (1)
{
printf("\n\t用方向键或w a s d键移动※推动★进入☆\n",cas+1);
printf("\n\t\t 共两关 第 %d 关\n",cas+1);
drawMap();
if (gameOver() == boxSum[cas])
{
cas++;
if (cas == 2)
break;
else
printf("\n\t\t 该关 OK!按任一键进继续\n");
}
keyDown();
system("cls");
}
printf("游戏结束!");
printf("\n");
system("pause");
return 0;
}
它存在的问题有:
代码重复:在 keyDown 函数中,处理上下左右移动的代码存在大量重复,这不仅增加了代码量,还使得代码难以维护。
边界检查缺失:在移动人和箱子时,没有对边界进行检查,可能会导致数组越界访问,从而引发程序崩溃。
胜负判断函数重复:gameOver 和 gameOver2 函数功能有重叠,增加了代码的冗余度。
代码可读性:部分变量命名不够清晰,降低了代码的可读性。
针对这些问题,我采用了将处理移动的代码提取到一个单独的函数 movePlayer 中,减少 keyDown 函数中的代码重复。同时,使用 findPlayer 函数来定位玩家的位置,提高代码的可读性。另外,
在 movePlayer 函数中,增加对边界的检查,确保不会发生数组越界访问。将 gameOver 和 gameOver2 函数合并为一个 isGameOver 函数,简化代码逻辑。使用更具描述性的变量名,如 currentLevel 表示当前关卡,boxCount 表示每关的箱子数,提高代码的可读性。
优化后的代码:#include <stdio.h>

include <stdlib.h>

include <conio.h>

// 定义地图元素

define EMPTY 0

define WALL 1

define GOAL 3

define BOX 4

define PLAYER 5

define BOX_ON_GOAL 7

define PLAYER_ON_GOAL 8

// 地图数据
int map[2][7][8] = {
{
1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 3, 1, 0, 1, 1, 3, 1,
1, 4, 0, 0, 4, 0, 3, 1,
1, 0, 1, 0, 1, 1, 4, 1,
1, 0, 0, 5, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1
},
{
1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 3, 1, 0, 1, 1, 3, 1,
1, 3, 4, 5, 4, 0, 3, 1,
1, 4, 1, 0, 1, 1, 4, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1
}
};

int currentLevel = 0; // 当前关卡
int boxCount[2] = {3, 4}; // 每关的箱子数

// 地图绘图
void drawMap() {
for (int i = 0; i < 7; i++) {
printf("\t\t");
for (int j = 0; j < 8; j++) {
switch (map[currentLevel][i][j]) {
case EMPTY:
printf(" ");
break;
case WALL:
printf("■");
break;
case GOAL:
printf("☆");
break;
case BOX:
printf("★");
break;
case PLAYER:
case PLAYER_ON_GOAL:
printf("※");
break;
case BOX_ON_GOAL:
printf("⊙");
break;
}
}
printf("\n");
}
}

// 定位玩家位置
void findPlayer(int *x, int *y) {
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 8; j++) {
if (map[currentLevel][i][j] == PLAYER || map[currentLevel][i][j] == PLAYER_ON_GOAL) {
*x = i;
*y = j;
return;
}
}
}
}

// 移动处理
void movePlayer(int dx, int dy) {
int playerX, playerY;
findPlayer(&playerX, &playerY);

int newX = playerX + dx;
int newY = playerY + dy;

// 检查边界
if (newX < 0 || newX >= 7 || newY < 0 || newY >= 8) return;

if (map[currentLevel][newX][newY] == EMPTY || map[currentLevel][newX][newY] == GOAL) {
    map[currentLevel][newX][newY] += PLAYER;
    map[currentLevel][playerX][playerY] -= PLAYER;
} else if (map[currentLevel][newX][newY] == BOX || map[currentLevel][newX][newY] == BOX_ON_GOAL) {
    int boxNewX = newX + dx;
    int boxNewY = newY + dy;

    // 检查箱子移动的边界
    if (boxNewX < 0 || boxNewX >= 7 || boxNewY < 0 || boxNewY >= 8) return;

    if (map[currentLevel][boxNewX][boxNewY] == EMPTY || map[currentLevel][boxNewX][boxNewY] == GOAL) {
        map[currentLevel][boxNewX][boxNewY] += BOX;
        map[currentLevel][newX][newY] += 1;
        map[currentLevel][playerX][playerY] -= PLAYER;
    }
}

}

// 按键处理
void keyDown() {
char ch = _getch();
switch (ch) {
case 'w':
case 'W':
case 72:
movePlayer(-1, 0);
break;
case 's':
case 'S':
case 80:
movePlayer(1, 0);
break;
case 'a':
case 'A':
case 75:
movePlayer(0, -1);
break;
case 'd':
case 'D':
case 77:
movePlayer(0, 1);
break;
}
}

// 胜负判断
int isGameOver() {
int count = 0;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 8; j++) {
if (map[currentLevel][i][j] == BOX_ON_GOAL) {
count++;
}
}
}
return count == boxCount[currentLevel];
}

int main() {
while (1) {
printf("\n\t用方向键或w a s d键移动※推动★进入☆\n");
printf("\n\t\t 共两关 第 %d 关\n", currentLevel + 1);
drawMap();

    if (isGameOver()) {
        currentLevel++;
        if (currentLevel == 2) {
            break;
        } else {
            printf("\n\t\t 该关 OK!按任一键继续\n");
            _getch();
        }
    }

    keyDown();
    system("cls");
}

printf("游戏结束!");
printf("\n");
system("pause");
return 0;

}
通过以上优化,使代码更加地健壮了,小游戏在运行时也不容易崩溃了。

posted on   xuanyee  阅读(2)  评论(0编辑  收藏  举报

编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示