Skip to content

Commit

Permalink
无聊用C++&Qt重写了一下
Browse files Browse the repository at this point in the history
  • Loading branch information
yaomer committed Apr 9, 2020
1 parent 501fbc9 commit a2feccf
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 0 deletions.
13 changes: 13 additions & 0 deletions C++/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// C++&Qt Version
//

#include "snake.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Snake snake;
snake.show();
return app.exec();
}
189 changes: 189 additions & 0 deletions C++/snake.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "snake.h"

void Snake::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
if (inithead) initSnake();
showFood();
if (findMinPath(head->s, food)) {
auto s = nextstep;
findtail = true;
if (findMinPath(s, tail->s)) {
growhead(s);
} else {
if (findMinPath(head->s, tail->s))
growhead(nextstep);
}
} else {
findtail = true;
if (findMinPath(head->s, tail->s))
growhead(nextstep);
}
findtail = false;
if (isEatFood()) creatFood();
else decrtail();
}

void Snake::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event);
this->repaint();
}

bool Snake::isCrash(Pos pos)
{
auto p = tail;
if (findtail) {
while (p) {
if (p != tail && pos == p->s) return true;
p = p->next;
}
} else {
while (p) {
if (pos == p->s) return true;
p = p->next;
}
}
return false;
}

bool Snake::isEatFood()
{
return head->s == food;
}

void Snake::initSnake()
{
inithead = false;
qsrand(::clock());
head = tail = new Node;
head->s.x = qrand() % (X2 - X1) + X1;
head->s.y = qrand() % (Y2 - Y1) + Y1;
showSnake();
creatFood();
}

void Snake::creatFood()
{
qsrand(::clock());
food.x = qrand() % (X2 - X1) + X1;
food.y = qrand() % (Y2 - Y1) + Y1;
if (isCrash(food)) creatFood();
showFood();
}

void Snake::showFood()
{
QPainter painter(this);
QPen pen;
pen.setColor(Qt::green);
pen.setWidth(P);
painter.setPen(pen);
painter.drawPoint(food.x, food.y);
}

void Snake::showSnake()
{
QPainter painter(this);
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(P);
painter.setPen(pen);
for (auto p = tail; p; p = p->next)
painter.drawPoint(p->s.x, p->s.y);
}

void Snake::growhead(Pos pos)
{
Node *p = new Node(pos);
head->next = p;
head = p;
showSnake();
}

void Snake::decrtail()
{
Node *p = tail->next;
delete tail;
tail = p;
}

void Snake::clear()
{
while (tail) {
Node *p = tail->next;
delete tail;
tail = p;
}
}

bool Snake::findMinPath(Pos p1, Pos p2)
{
return AStar(p1, p2);
}

#define isOk(x, y) ((x) >= X1 && (x) <= X2 && y >= Y1 && (y) <= Y2)

bool Snake::AStar(Pos p1, Pos p2)
{
static bool open[X2][Y2];
static bool close[X2][Y2];
bzero(open, sizeof(open));
bzero(close, sizeof(close));

AStarPos s(p1.x, p1.y);
AStarPos d(p2.x, p2.y);
openlist.push(s);
open[s.x][s.y] = true;

static int dx[] = { 0, 0, 1, -1 };
static int dy[] = { 1, -1, 0, 0 };

bool found = false;
while (!openlist.empty()) {
auto p = openlist.top();
openlist.pop();
close[p.x][p.y] = true;
for (int i = 0; i < 4; i++) {
int tx = p.x + dx[i];
int ty = p.y + dy[i];
Pos pos(tx, ty);
if (!close[tx][ty] && isOk(tx, ty) && !isCrash(pos)) {
if (!open[tx][ty]) {
AStarPos ts(tx, ty);
ts.g = p.g + 1;
ts.h = ::abs(tx - d.x) + ::abs(ty - d.y);
ts.f = ts.g + ts.h;
parent.emplace(ts, p);
openlist.push(ts);
open[tx][ty] = true;
}
}
if (p.x == d.x && p.y == d.y) {
backpath(s, d);
found = true;
goto end;
}
}
}
end:
while (!openlist.empty())
openlist.pop();
parent.clear();
return found;
}

void Snake::backpath(const AStarPos &s, const AStarPos &d)
{
auto lastpos = d;
while (true) {
auto it = parent.find(lastpos);
if (it == parent.end()) break;
if (it->second == s) {
nextstep.x = it->first.x;
nextstep.y = it->first.y;
break;
}
lastpos = it->second;
}
}
122 changes: 122 additions & 0 deletions C++/snake.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#ifndef _SNAKE_H
#define _SNAKE_H

#include <QWidget>
#include <QApplication>
#include <QPainter>

#include <queue>
#include <vector>
#include <unordered_map>

struct Pos {
int x, y;
Pos() = default;
Pos(int x, int y)
{
this->x = x;
this->y = y;
}
bool operator==(const Pos& pos) const
{
return x == pos.x && y == pos.y;
}
};

struct Node {
Node() = default;
Node(const Pos& pos) : s(pos)
{
}
Pos s;
Node *next = nullptr;
};

struct AStarPos {
int x, y; // 坐标
int f, g, h; // for A*
AStarPos() = default;
AStarPos(int x, int y)
{
this->x = x;
this->y = y;
f = g = h = 0;
}
AStarPos& operator=(const AStarPos& pos)
{
memcpy(this, &pos, sizeof(pos));
return *this;
}
bool operator==(const AStarPos& pos) const
{
return x == pos.x && y == pos.y;
}
};

struct ASPCompare {
bool operator()(const AStarPos& p1, const AStarPos& p2) const
{
return p1.f > p2.f;
}
};

struct ASPHash {
size_t operator()(const AStarPos& p) const
{
return p.x * 31 + p.y * 13;
}
};

#define P 18
#define XW 300
#define YW 300
#define X1 (P / 2)
#define Y1 (P / 2)
#define X2 (XW - P / 2)
#define Y2 (YW - P / 2)

// 整个蛇的构造是这样的:
// O -> O -> O -> O -> O
// tail head
// 运动时会在head后添加一个新节点,从tail处移除一个节点
class Snake : public QWidget {
Q_OBJECT
public:
Snake() : head(nullptr), tail(nullptr), findtail(false), inithead(true)
{
this->setWindowTitle("snakeAI");
this->resize(XW, YW);
this->startTimer(10);
}

protected:
// void keyPressEvent(QKeyEvent *event) override ;
void paintEvent(QPaintEvent *event) override ;
void timerEvent(QTimerEvent *event) override ;

private:
void initSnake();
void creatFood();
void showFood();
void showSnake();
void growhead(Pos pos);
void decrtail();
void clear();

bool isCrash(Pos pos);
bool isEatFood();

bool AStar(Pos p1, Pos p2);
bool findMinPath(Pos p1, Pos p2);
void backpath(const AStarPos& s, const AStarPos& d);
// bool findMaxPath(Pos p1, Pos p2);
std::priority_queue<AStarPos, std::vector<AStarPos>, ASPCompare> openlist;
std::unordered_map<AStarPos, AStarPos, ASPHash> parent;

Node *head, *tail;
Pos nextstep, food;
bool findtail;
bool inithead;
};

#endif // _SNAKE_H
31 changes: 31 additions & 0 deletions C++/snake.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
QT += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
main.cc \
snake.cc

HEADERS += \
snake.h

FORMS += \
mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit a2feccf

Please sign in to comment.