C编程实践:推箱子游戏
这是我以前学习C语言时的一个练习,当然是参考了网上很多例子才搞出来的(我这个之前是参考C语言中文网的例子)。
推箱子源码很多,大家都玩得烂了,但这种大家玩得烂的东西是很重要的学习资料,因为例子很多,有利于我们进行学习。
做这个推箱子小游戏我们可以思考两个问题:一是地图怎么来的;二是推箱子的小人移动问题。
针对第一个问题:
当时做笔记挺认真,竟然还手写代码
。地图是由特殊字符绘制出来的。
为此,创建一个地图绘制的函数,这个函数传入一个二维数组,其中这个二维数组里保存的就是特殊字符的位置。
当时在这里碰到了一个问题:二维数组作为函数参数的问题
。大家回想一下自己之前有没有用过多维数组作为函数参数?
我之前是没有用过的,在写这个地图布局函数时习惯性的像操作一维数组那样子来操作二维数组,结果就出错了。下面简单来分析这个问题:
假如我们要构建一个函数来打印二维数组元素,我们可能会这么写:
void func1(int **array, int m, int n)
{
int i = 0, j = 0;
for ( i = 0; i < m; i++ )
{
for ( j = 0; j < n; j++ )
{
//printf("%d ", array[i][j] );
printf("%d ", *(*(array+i)+j) );
}
}
}
int main(void)
{
int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
func1((int**)a, 3, 3);
return 0;
}
这么写看起来好像没什么问题,但是实际去运行的时候就发现有问题了。大家觉得输出结果会是什么呢?
结果是什么都没输出。出错原因是因为二维数组作为函数参数时要给出二维长度
。
我们需要定义一个指针数组,该指针数组中的元素分别指向每一行的第一个元素。再把该指针数组作为形参传入func1函数中。即主函数修改为如下所示:
int main(void)
{
int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
int *p[3];
p[0] = &a[0][0]; //a[0]
p[1] = &a[1][0]; //a[1]
p[2] = &a[2][0]; //a[2]
func1(p, 3, 3);
return 0;
}
除此之外,还有如下三种方法:
(1)形参为一级指针
void func2(int *array, int m, int n)
{
int i = 0;
for ( i = 0; i < m*n; i++ )
{
printf("%d ", array[i]);
}
}
(2)形参给出二维长度
void func3(int array[][3], int len)
{
int i = 0, j = 0;
for ( i = 0; i < len; i++ )
{
for ( j = 0; j < 3; j++ )
{
printf("%d ", array[i][j]);
}
}
}
(3)形参声明为指向数组的指针(数组指针)
void func4(int (*array)[3], int len)
{
int i = 0, j = 0;
for ( i = 0; i < len; i++ )
{
for ( j = 0; j < 3; j++ )
{
printf("%d ", array[i][j]);
}
}
}
最终,地图布局函数可写为:
void map_layout(int **array, int m, int n)
{
int i = 0, j = 0;
for ( i = 0; i < m; i++ )
{
for ( j = 0; j < n; j++ )
{
switch(array[i][j])
{
case 0: set_pattern(" ", 0xF); break;//特殊字符是普通字符的两倍
case 1: set_pattern("■", 8); break;
case 2: set_pattern("■", 4); break;
case 3: set_pattern("♀", 3); break;
case 4: set_pattern("★", 0xE); break;
case 5: set_pattern("★", 6); break;
case 6: set_pattern("♀", 3); break;
default: break;
}
}
printf("\n");
}
}
这里用到特殊字符,这些字符长度是普通字符的两倍。打印输出这些需要用%S
,如:
void set_pattern(char *str, short int color)
{
set_color(color);
printf("%s", str);
}
整个游戏过程中,玩家时刻都是在控制人物的移动。所以,程序中应为人物为出发点,采用穷举法罗列出所有由于人物移动而导致的情况。
/*
人物在移动过程中,无论人物是向哪个方向移动,程序中都可归为一种情况:人物在向前移动。穷举:
1、人位于空格处:
(1)人前是箱子,箱子在空格上,箱子前面是空格
(2)人前是箱子,箱子在空格上,箱子前面是目标位置
(3)人前是箱子,箱子在目标位置上,目标位置前面是空格
(4)人前是箱子,箱子在目标位置上,目标位置前面是目标位置
(5)人前是空格
(6)人前是目标位置
2、人位于目标位置处:同上
*/
void control_move(int x1, int y1, int x2, int y2)
{
//人位于空格处
if( map[x][y] == 3 )
{
//人前是箱子,箱子在空格上
if( map[x1][y1] == 2 )
{
//箱子前面是空格
if( map[x2][y2] == 0 )
{
map[x][y] = 0;
map[x1][y1] = 3;
map[x2][y2] = 2;
}
//箱子前面是目标位置
if( map[x2][y2] == 4 )
{
map[x][y] = 0;
map[x1][y1] = 3;
map[x2][y2] = 5;
}
}
//人前是箱子,箱子在目标位置上
if( map[x1][y1] == 5 )
{
//箱子前面是空格
if( map[x2][y2] == 0 )
{
map[x][y] = 0;
map[x1][y1] = 6;
map[x2][y2] = 2;
}
//箱子前面是目标位置
if( map[x2][y2] == 4 )
{
map[x][y] = 0;
map[x1][y1] = 6;
map[x2][y2] = 5;
}
}
//人前是空格
if( map[x1][y1] == 0 )
{
map[x][y] = 0;
map[x1][y1] = 3;
}
//人前是目标位置
if( map[x1][y1] == 4 )
{
map[x][y] = 0;
map[x1][y1] = 6;
}
}
//人位于目标位置
if( map[x][y] == 6 )
{
//人前是箱子,箱子在空格上
if( map[x1][y1] == 2 )
{
//箱子前面是空格
if( map[x2][y2] == 0 )
{
map[x][y] = 4;
map[x1][y1] = 3;
map[x2][y2] = 2;
}
//箱子前面是目标位置
if( map[x2][y2] == 4 )
{
map[x][y] = 4;
map[x1][y1] = 3;
map[x2][y2] = 5;
}
}
//人前是箱子,箱子在目标位置上
if( map[x1][y1] == 5 )
{
//箱子前面是空格
if( map[x2][y2] == 0 )
{
map[x][y] = 4;
map[x1][y1] = 6;
map[x2][y2] = 2;
}
//箱子前面是目标位置
if( map[x2][y2] == 4 )
{
map[x][y] = 4;
map[x1][y1] = 6;
map[x2][y2] = 5;
}
}
//人前是空格
if( map[x1][y1] == 0 )
{
map[x][y] = 4;
map[x1][y1] = 3;
}
//人前是目标位置
if( map[x1][y1] == 4 )
{
map[x][y] = 4;
map[x1][y1] = 6;
}
}
}
运行结果:
最后
如果觉得文章不错,转发、在看,也是我们继续更新得动力。