-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
355 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.