完全用链表实现的贪吃蛇.docx
- 文档编号:2263944
- 上传时间:2023-05-03
- 格式:DOCX
- 页数:23
- 大小:77.77KB
完全用链表实现的贪吃蛇.docx
《完全用链表实现的贪吃蛇.docx》由会员分享,可在线阅读,更多相关《完全用链表实现的贪吃蛇.docx(23页珍藏版)》请在冰点文库上搜索。
完全用链表实现的贪吃蛇
完全用链表实现的贪吃蛇
1.链表设计
同事突然说想实现一个贪吃蛇,这使我想起了几年前实现的一个很糟糕的贪吃蛇程序,代码可以在《一个java写的贪吃蛇程序》里面找到。
如今,突然想再实现一个贪吃蛇,不过这次绝对不能再那么糟糕了。
用链表实现并且只用链表实现贪吃蛇是一个不错的主意,于是初步的打算就是先规划出到底需要什么链表,图示如下:
游戏面板上的所有的元素都处于一条或者多条链表之中,这样整个游戏的操作就简单了,无非就是将元素在链表之间移动来移动去。
另外一个要点就是将“位置”信息作为静态数据保存,将元素本身和元素的位置完全分离,只是将位置当成元素的一个属性信息。
2.数据结构
到底用什么链表呢?
Linux内核的“侵入式链表”list_head是一个不错的选择,这样就不用管理大量的链表节点内存了,因为“本身即链表”。
因此设计以下的数据结构:
2.1.位置结构
viewplain
1.struct posision {
2. int x;
3. int y;
4.};
很简单,没什么好说的,坐标而已,也可以扩展成三维坐标。
需要注意,方向信息也作为一个posision保存,之所以可以这样做是因为posision是一个坐标,而一个坐标在坐标系中可以表示一个失量,矢量的含义就是有方向的数量,因此posision既可以表示位置,又可以表示方向。
2.2.节点结构
viewplain
1.struct body {
2. struct list_head list;
3. struct posision pos;
4. struct posision direct;
5. void (*go_on) (struct body* this);
6. void *private;
7.};
其中go_on回调函数实现了蛇的前进动作,但是由于像障碍,边墙,食物本身也是链表元素,因此需要一个private数据来做扩展。
需要指明的是,整个游戏中还有一个隐含的链表,那就是“拐弯”处的节点,这个链表记录了蛇身体该在哪个地方拐弯,因此很显然,拐弯链表是需要和键盘按键侦听器联系在一起的。
2.3.游戏面板
viewplain
1.struct pad {
2. struct list_head snake_body;
3. struct list_head changes;
4. struct list_head kill_body;
5. struct list_head food;
6. unsigned int curr_status;
7. void (*game_handler) (struct pad* this);
8. void (*key_handler) (struct pad* this, struct posision key);
9. void (*random_gen_food) (struct pad* this);
10. void (*random_gen_fence) (struct pad* this);
11. void (*eat) (struct pad* this);
12.};
该数据结构囊括了整个游戏的面板以及操作。
2.4.代码初步实现
有了上述的数据结构,实现代码就很简单了
首先,go_on的逻辑就是当前位置加上方向矢量
viewplain
1.void gen_go_on(struct body* this)
2.{
3. sb->pos.x += sb->direct.x;
4. sb->pos.y += sb->direct.y;
5.}
键盘处理就是根据不同的按键来生成方向信息
viewplain
1.void gen_key_handler(struct pad* game, unsigned int key)
2.{
3. //{0,-1},{0,1},{-1,0},{1,0} //这几个是静态的方向数据,全部是posision结构体
4. //ALLOC body -> cb
5. list_add (cb, &(game->changes));
6.}
食物生成比较复杂,但是最简单的实现就是随机在面板的空闲链表中取出一个加入食物链表,我的这个贪吃蛇实现中,不但食物一次可以有多份,体现蛇的贪婪,并且还可以设置物体障碍,表现蛇玩命贪吃。
viewplain
1.void gen_random_gen_food(struct pad* game)
2.{
3. //random pos avoid snake_body or kill_body
4. //ALLOC body -> fb
5. list_add (fb, &(game->food));
6.}
最重要的就是以下这个game_handler了,它处理游戏的中心逻辑
viewplain
1.unsigned int game_handler()
2.{
3. struct list_head *snake;
4. //以下的遍历判断蛇节点是否需要拐弯
5. list_for_each(snake, &(g_pad->snake_body)){
6. struct body *sb = list_entry(snake, struct body, list);
7. struct list_head *change;
8. list_for_each(change, &(g_pad->changes)){
9. struct body *cp = list_entry(change, struct body, list);
10. if (sb->pos.x == cp->pos.x && sb->pos.y == cp->pos.y) {
11. sb->direct = cp->direct;
12. if (snake->next == &snake_body) {
13. list_del (change);
14. }
15. break;
16. }
17. }
18. (*sb->go_on)(sb);
19. }
20. //以下的遍历判断蛇头是否碰到自身或者边墙
21. struct body *head = snake_body.next;
22. struct list_head *kb;
23. list_for_each(kb, &(g_pad->kill_body)){
24. struct body *cp = list_entry(kb, struct body, list);
25. if (head->pos.x == cp->pos.x && head->pos.y == cp->pos.y) {
26. g_pad->curr_status = 1;
27. }
28. }
29. //以下的遍历判断蛇头是否碰到了食物,然后吃掉它
30. struct list_head *fd;
31. list_for_each(fd, &(g_pad->food)){
32. struct body *cp = list_entry(fd, struct body, list);
33. if (head->pos.x == cp->pos.x && head->pos.y == cp->pos.y) {
34. list_del (cp);
35. list_add (cp, &(g_pad->snake_body));
36. (*g_pad->eat)(g_pad);
37. (*g_pad->random_gen_food)(g_pad);
38. }
39. }
40.}
最后需要一个处理按键的函数
viewplain
1.void key_handler()
2.{
3. //get key
4. (*g_pad->key_handler)(g_pad, key_pos);
5.}
最终我们发现还缺点什么,那就是这个实现测试起来很麻烦,还需要单独抽取linux内核的list.h。
3.Java实现
由于用C语言编写的代码在测试的时候需要图形库,而我最烦的就是GUI编程了,关键是因为自己太懒了,而GUI环境部署又需要一定的工作量,因此难耐了。
在别人看来,部署一个qt开发环境是小菜一碟,在我看来比登天还难...
后来想在Windows平台搞,可以编译起来出了那么多的问题,不是缺这就是少那...最终,还是java是避开GUI环境的好办法。
java的好处是环境部署最简单,因此还是用java来实现吧,和2004年实现的那个一样。
虽然java中没有list_head,但是由于其本身就是面向对象的,且容器支持泛型,因此使用LinkedList也是不错的。
由于贪吃蛇的中心逻辑不在UI,因此也就只是简单的实现了一个UI,代码如下:
viewplain
1.import java.awt.*;
2.import java.awt.event.*;
3.import javax.swing.*;
4.import java.util.*;
5.
6.class posision {
7. int x;
8. int y;
9. public posision(){}
10. public posision(int x, int y) {
11. this.x = x;
12. this.y = y;
13. }
14.}
15./**
16. * 静态数据,保存所有的位置和方向信息
17. * 这原本是可以定义到各个使用类内部以内部类实现的
18. * @author marywangran
19. */
20.class static_pos {
21. public static posision LEFT = new posision(-1, 0);
22. public static posision RIGHT = new posision(1, 0);
23. public static posision UP = new posision(0, -1);
24. public static posision DOWN = new posision(0, 1);
25. public static posision HALT = new posision(0, 0);
26. public static posision pos[][];
27. public static void setpos (int width, int height) {
28. int i = 0, j = 0;
29. int num = width*height;
30. pos = new posision[width][height];
31. for (i = 0; i < width; i++) {
32. for (j = 0; j < height; j++) {
33. pos[i][j] = new posision(i, j);
34. }
35. }
36. }
37. public static posision go_on(posision curr, posision direct) {
38. return pos[curr.x + direct.x][curr.y + direct.y];
39. }
40.}
41./**
42. * 游戏中所有的元素都是链表,定义为body类
43. * @author marywangran
44. */
45.class body extends Object{
46. posision pos;
47. posision direct;
48. extension ext;
49. public body(){}
50. public body (posision pos, posision direct){
51. this.pos = pos;
52. this.direct = direct;
53. }
54. boolean go_on (boolean change, posision pos) {
55. if (change)
56. this.pos = static_pos.go_on(this.pos, this.direct);
57. else {
58. return (static_pos.go_on(this.pos, this.direct).x == pos.x)&&
59. (static_pos.go_on(this.pos, this.direct).y == pos.y);
60. }
61. return true;
62. }
63. void set_extension(extension ext) {
64. this.ext = ext;
65. }
66. extension get_extension() {
67. return this.ext;
68. }
69. //void *private;
70.}
71./**
72. * 对应于C代码的void *private;每一个节点的可能有的所有属性
73. * @author marywangran
74. */
75.interface extension {
76. public void set_timeout(int timeout);
77. public long get_timeout();
78. public long star_time();
79. public void set_score(int score);
80. public int get_score();
81.}
82./**
83. * 食物的属性
84. * @author marywangran
85. *
86. */
87.class food_extension implements extension {
88. long time_out;
89. int score;
90. long start;
91. public void set_timeout(int timeout) {
92. this.time_out = timeout*1000;
93. this.start = System.currentTimeMillis();
94. }
95. public long get_timeout() {
96. return this.time_out;
97. }
98. public long star_time(){
99. return this.start;
100. }
101. public void set_score(int score) {
102. this.score = score;
103. }
104. public int get_score() {
105. return this.score;
106. }
107.}
108./**
109. * 完全由链表实现的贪吃蛇游戏类,其本质就是处理body链表
110. * @author marywangran
111. */
112.class Snake_Game_Pad {
113. LinkedList
snake_body;114. LinkedList
change_body;115. LinkedList
kill_body;116. LinkedList
food_body;117. LinkedList
free_body;118. LinkedList
119. int curr_status;
120. int curr_level;
121. int curr_score;
122. int width, height;
123. public Snake_Game_Pad () {
124. snake_body = new LinkedList();
125. change_body = new LinkedList();
126. kill_body = new LinkedList();
127. food_body = new LinkedList();
128. free_body = new LinkedList();
129. replace_body = new LinkedList();
130. }
131. /**
132. * 贪吃蛇游戏初始化
133. * @param width 横向元素数量
134. * @param height 纵向元素数量
135. * @param init_length 初始蛇长度
136. */
137. public void init(int width, int height, int init_length) {
138. this.width = width;
139. this.height = height;
140. int i = 0, j = 0;
141. for (i = 0; i < this.width; i++){
142. for (j = 0; j < this.height; j ++) {
143.
144. free_body.add(new body(static_pos.pos[j][i], static_pos.HALT));
145. }
146. }
147. for (i = 0; i < this.width; i++){
148. body kb1 = free_body.get(0);
149. body kb2 = free_body.get(free_body.size()-1);
150. free_body.remove(kb1);
151. kill_body.add(kb1);
152. free_body.remove(kb2);
153. kill_body.add(kb2);
154. }
155. for (i = 0; i < this.height-2; i ++) {
156. body kb1 = free_body.get(i*(this.width-2));
157. body kb2 = free_body.get(i*(this.width-2)+this.width-1);
158. free_body.remove(kb1);
159. kill_body.add(kb1);
160. free_body.r
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 完全 用链表 实现 贪吃
![提示](https://static.bingdoc.com/images/bang_tan.gif)