贪吃蛇项目总结

贪吃蛇的实现,实现了几遍。从开始的举步维艰到最终的思路清晰,都是不容易的。这里不想针对整个工程大谈。只针对里面的三点进行叙述,便是产生食物时,用到一个算法,也叫洗牌算法,以及整体上的一维数组的使用。而其中的一维数组的使用,而不是二维数组。这里也是为了方便编程,以及提高数据的处理速度。其实在我看来,数组的灵活使用,其实就是下标的灵活使用。也就是说,玩数组就是玩下标。再有一点便是程序的整体上,很多人有一个误区,具体就是处理图形的方式上有所错误理解。接下来一一叙述。

1.洗牌算法

注:这里为了画图以及讲述方便,假设只有10张牌,分别为0-9.

 方式一:在10以内产生一个随机数,然后放到第一张牌,即下标为0处;

               在10以内再产生一个随机数,然后放到第二张牌上,即下标为1处;

              ......

上面这种方式,无非是最简单的方式,但却有一个致命的缺点。即从第二次开始便要查重,而且随着循环的继续,重复的可能性就会越来越高,显然是不合理的。这里在第一种方式的基础上改进,将产生的牌作为最后一张牌,然后每次的基础上产生随机出的范围减一,这样便轻松产生一副随机的牌,而且不需要查重的。对于这个思想,发现这种不叫“洗牌”,因为没有洗。下面给出的方式二,是在这种思路的基础上的,才是真正的洗牌,而这种办法才是具有通用性的。

方式二:首先需要先准备一个一维数组,按照这里的10张牌,即长度为10,依次赋值为下标的值(如图所示),这里赋值为下标的的值,说明之前为一副新牌,而在实际应用中,这里便是用来记录原数据的。贪吃蛇项目总结_第1张图片

              在10以内随机产生一个数,以这个数为下标,将下标所对应的这个值与最后一个值交换;

             在9以内随机产生一个值,以这个数为下标,将下标所对应的这个值与倒数第二个值交换;

             ......

 

 

这种方式既可以避免查重,而且可以对原数据进行真正意义上的洗牌,时间复杂度为O(n)。代码如下:

    int i;
    int temp;
    int randNum;
    int arr[10];
    int count = 10;

    for(i = 0; i < 10; i++) {
        arr[i] = i;
    }

    srand(time(NULL));
    for(i = 0; i < 10; i++) {
        randNum = rand() % count;
        temp = arr[randNum];
        arr[randNum] = arr[count - 1];
        arr[count - 1] = temp;
        count--;
    }

    for(i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }

由于洗牌算法在贪吃蛇中的使用牵扯众多,因此在最后详谈。

2.一维数组的使用

一维数组的使用也是源于之前的经验。首先说明,这里的一维数组只是为了方便运算,实际是用一维数组表示二维数组,只不过在转换为坐标时,进行一步运算即可。在贪吃蛇游戏设计过程中,前几次编程时,对于其中的食物的产生以及蛇咬到自己的身体或者对手的身体,这个处理,总是很复杂。因为每一次移动或者产生食物时,需要对身体进行遍历,以及食物的坐标也是需要遍历的。这就会产生很大的时间复杂度。对于这个问题,最终想到一个办法,那就是给一个全局的数组,用来记录屏幕上点的当前状态,至于好几种状态(食物,自己蛇身或蛇头,对手蛇身或蛇头,空地,障碍物墙壁),可以给好几个宏,以记录。这样的话,每次移动或者产生食物时,只需改变相应的几个点的坐标,而在处理之前的几个问题时,就可以直接用下标判断,一句搞定。没有代码很难讲清楚,后面我会用代码详谈。

3.覆盖法

覆盖法只是这么一个叫法。实际想说的是在每次移动蛇或产生食物时,可以用覆盖法代替刷新屏幕的方法。网上很多人的贪吃蛇每次刷新整个屏幕,实际上复杂了问题,因为每次只有几个点在变化。我们可以直接通过覆盖来达到我们的目的。至于具体的就不详说了,可以在最后看看代码。

洗牌算法二叙:

贪吃蛇项目在屏幕信息数组上实际上有两个,一个便是上面的全局的一维数组,因为屏幕上共2000个点,因此便是申请了一个长度为2000的全局一维数组以记录信息,而在产生食物时,还需要有一个局部的长度为两千的一维数组,这个数组就是之前洗牌算法中的需要的数组。这个数组只是为了产生随机的并且不重复的食物点阵信息而存在。先给出这块的代码,再拿代码讲述吧。

//产生食物的坐标
void getFoodXy(int *point) {
	int randNum;
	int i;
	int count = 0;
	int array[2000] = {0};
	int j = 1999;

	for(i = 0; i < 2000; i++) {
		if(point[i] == 0) {
			array[count] = i;
			count++;
		}
	}

	for(i = 0; i < FOODNUM; i++) {
		srand(time(0) + i);
		randNum = rand() % count;
		point[array[randNum]] = FOOD;
		array[randNum] = array[j--];
		count--;
	}
}

这里可以看到得到随机食物的函数相当简单。这便是得益于一维数组和发牌算法的支持。如果按照一般的实现方案,你需要剔除掉蛇身,蛇头,边框,还有剩余的食物,然后再在里面产生食物。我开始也是按照这种方法实现的,这里的代码相当复杂,繁琐,令我恶心之至。不过加上犀利的操作后,一切变得简单方便。可见这几部操作的重要性。

 

 

完整代码已发至Github上

https://github.com/kennDJ/snake

有不同意见,欢迎交流!!!

你可能感兴趣的:(数据结构与算法)