代码改变世界

重构一个贪吃蛇游戏(linux c++)

2010-08-31 18:37  BAsil  阅读(2433)  评论(2编辑  收藏  举报

最近读dave_cn[原创]分享自己写的一个贪吃蛇的游戏(Linux) ,C程序写的,就想着用C++重写一下,把面向对象的思想也引入进来。

这里感谢dave_cn,重构后应该有40%的代码是重用的,省了不少功夫。

程序继续采用了ncurses.h库,关于ncurses猛击这里。不知道在window下有没有类似的库(查了一下,可以用conio.h在类dos窗口下实现,有空实现一个window版)。

第一次在linux下用c++写东西,碰到的问题比较多,好歹都解决了,这里也一并做记号。

snake

需求比较简单,直接上UML图

uml

Screen 类 主要负责定时刷新
Snake类    footprintx,footprinty  由于蛇移动实际就是重绘窗口,所以需要把它走过的痕迹擦掉(用空字符代替*即可),只记录一步痕迹
                list  蛇的身体,采用了双链表,关于双链表猛击这里,代码中调用 dlinkedList.h
                bodyReshape()  每一步移动时,其实就是蛇身体各部位坐标的移动,bodyReshape修改各部位坐标
                eraseFootPrint() 擦除痕迹
                isClapseArea() 是否走到边界
                isSpiralDead() 是否头部碰到身体
                lifeProbing()  生命迹象探测,就是看看是不是还活着
                move()   移动,在移动的过程会做一些工作,详见时序图
                receivingNavi() 接受键盘控制蛇头移动方向
                selfGeneration() 吃到Block后,身体张长
LivingArea类 蛇的活动区域
                 Block 障碍物,由于一次只有一个障碍,所以LivingArea和Block是1对1的
                 blockChecking() 检测障碍物
                 isEattingPhase() 是否是吃Block的时刻,当蛇头和Block的坐标相同,即认为是isEattingPhase=true
                 isDigestPhase() 未用
                 eraseBlock() 将isBlockExisted置为false
                 generateBlock() 生成Block
再看一下时序图

sequence

关于蛇的身体,原c程序采用的是一个倒置的首尾相连的链表,head->front指向snake的尾部
如: [a]<-[b]<-[c]<-[d]    a为head
       |                     ^     snake移动的时候,只用head指向d,
       `-------------- '     并且修改d的(y,x)为snake头移动到的位置.
这样的好处是在移动时(身体不增长时),只需要把不用的节点(即上面提到的痕迹)设为头节点,修改其坐标为头的坐标

重构的时候为了简单,用了双链表的源代码,简单的删除痕迹节点,增加新节点,效率不高,简单粗暴。

原c代码

void movesnake(struct TSnake *psnake)
{
 int hy = psnake->head->y;
 int hx = psnake->head->x;
 
 psnake->head = GetSnakeTail(psnake);
 
 switch (psnake->dir) {
 case DIR_UP:
     psnake->head->y = hy - 1;
     psnake->head->x = hx;
     break;
 
 case DIR_DOWN:
     psnake->head->y = hy + 1;
     psnake->head->x = hx;
     break;
 
 case DIR_LEFT:
     psnake->head->y = hy;
     psnake->head->x = hx - 1;
     break;
 
 case DIR_RIGHT:
     psnake->head->y = hy;
     psnake->head->x = hx + 1;
     break;
     
 default:
     break;
 }
}

重构后c++代码

void Snake::bodyReshape()
{
  SnakeNode t;
  list.pop_back(t);
  footprinty=t.y;
  footprintx=t.x;
  list.retrieve(0,t);
  switch (dir)
  {
   case DIR_UP:
     t.y = t.y - 1;
     t.x = t.x;
     break;

   case DIR_DOWN:
     t.y = t.y + 1;
     t.x = t.x;
     break;

   case DIR_LEFT:
     t.y = t.y;
     t.x = t.x - 1;
     break;

   case DIR_RIGHT:
     t.y = t.y;
     t.x = t.x + 1;
     break;

   default:
     break;
  }
  list.insert(0,t);
  
}

编译:

sudo g++ -o mySnake mySnake.cpp Screen.cpp LivingArea.cpp Snake.cpp SnakeNode.cpp Block.cpp –lncurses

源代码下载