实现贪吃蛇功能:
1.蛇节点成员:
class SnakeNode
{
public:
SnakeNode(Point data = Point{ INVALID, INVALID }, SnakeNode* pred = nullptr, SnakeNode* succ = nullptr)
: position(data), pred(pred), succ(succ) {}
~SnakeNode();
Point position;
SnakeNode* pred;
SnakeNode* succ;
SnakeNode* insertAsPred(Point const& data);
SnakeNode* insertAsPred(SnakeNode* node);
SnakeNode* insertAsSucc(Point const& data);
SnakeNode* insertAsSucc(SnakeNode* node);
SnakeNode* popPred();
SnakeNode* popSucc();
Point getUp() const;
Point getDown() const;
Point getLeft() const;
Point getRight() const;
void draw(HDC& hdc);
};
2.蛇的所包含的成员:
class SnakeNode;
class Snake
{
private:
int _len; // 蛇长度
SnakeNode* header; // 头哨兵
SnakeNode* trailer; // 尾哨兵
Direction direction;
LPRECT rect;
protected:
// 蛇的初始化
void init();
// 删除所有蛇节点
int clear();
//删除合法位置p处的节点,返回被删除节点
Point remove(SnakeNode* node);
SnakeNode* insertAsBegin();
SnakeNode* insertAsBegin(SnakeNode* node);
Point getMoveNextPoint() const;
SnakeNode* popEnd();
SnakeNode* popBegin();
SnakeNode* begin() const;
public:
Snake(HWND& hWnd);
~Snake();
// 蛇的移动
void move();
// 画蛇
void draw(HDC& hdc);
};
3.将贪吃蛇设置成链表的形式
将贪吃蛇设置成双向链表,初始化时设置一个头哨兵与尾哨兵,不包含任何数据,并随机生成一个节点,表示游戏开始时,贪吃蛇的位置
Snake::Snake(HWND& hWnd)
: _len(0)
, header(nullptr)
, trailer(nullptr)
, direction(UNKNOWN)
, rect(nullptr)
{
init();
rect = new tagRECT;
GetClientRect(hWnd, rect);
int x = rand() % ((rect->right - rect->left) / 10);
int y = rand() % ((rect->bottom - rect->top) / 10);
header->insertAsSucc(Point(x * 10, y * 10));
}
void Snake::init()
{
header = new SnakeNode;
trailer = new SnakeNode;
header->succ = trailer;
trailer->pred = header;
_len = 0;
}
4.移动时,贪吃蛇的移动方法(采用滚动的形式):
将尾节点从链表中pop,并insert到链表的首节点
具体实现:采用滚动的形式,通过尾哨兵获取末尾节点end,并从链表中去掉,根据蛇的运动方向,获取下一步所经过的屏幕的position,将end的position设置成蛇运动下一步所经过的屏幕的position,插入作为到整个蛇链表的首节点的前驱
void Snake::move()
{
Point begin = getMoveNextPoint();
SnakeNode* node = popEnd();
node->position = begin;
insertAsBegin(node);
}
SnakeNode* Snake::popEnd()
{
SnakeNode* node = trailer->popPred();
_len--;
return node;
}
Point Snake::getMoveNextPoint() const
{
if (begin()->position == Point(INVALID, INVALID))
return Point(INVALID, INVALID);
switch (direction)
{
case UP:
return begin()->getUp();
case DOWN:
return begin()->getDown();
case LEFT:
return begin()->getLeft();
case RIGHT:
return begin()->getRight();
default:
return Point(INVALID, INVALID);
}
}
5.当蛇吃到食物时:
吃到食物的位置,根据下一步蛇移动的屏幕位置,插入到首节点之前,,此时,蛇不移动,只是增长一个节点,再下一时刻才移动
// 当吃到食物时,在begin插入节点
SnakeNode* Snake::insertAsBegin()
{
Point p = getMoveNextPoint();
if (p == Point(INVALID, INVALID))
return nullptr;
SnakeNode* node = header->insertAsSucc(p);
_len++;
return node;
}
总结:
通过此次学习,让我在实际应用问题中使用数据结构更加熟练,形成了学习到应用的转化。不再像此前思维固化,只知道蛮力求解问题,让我的思维更加灵活和懂得变通