diff options
Diffstat (limited to 'src/snake.cpp')
-rw-r--r-- | src/snake.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/snake.cpp b/src/snake.cpp new file mode 100644 index 0000000..6f18670 --- /dev/null +++ b/src/snake.cpp @@ -0,0 +1,172 @@ +#include "snake.h" + +#include <QWidget> +#include <QPaintEvent> +#include <QPainter> +#include <QRectF> +#include <QTimer> +#include <QDebug> + +#define SCALING (16) + +#define DIR_UP 0 +#define DIR_DOWN 1 +#define DIR_LEFT 2 +#define DIR_RIGHT 3 + +#define FIELD(x,y) _field[(x) + ((y) * _width)] +#define FIELD_FREE 0 +#define FIELD_SNAKE 1 +#define FIELD_FOOD 2 + +Snake::Snake(QWidget *widget) + : _widget(widget), + _field(nullptr), + _direction(DIR_UP) +{ + _width = widget->width() / SCALING; + _height = widget->height() / SCALING; + _snakeLen = 5; + if (_width <= 0 || _height <= 0) + return; + _field = (int*)calloc(_width * _height, sizeof(*_field)); + _x = _y = -1; + qDebug() << "Field:" << _width << _height; + QTimer *t = new QTimer(widget); + t->start(100); + // Game logic + QTimer::connect(t, &QTimer::timeout, [this]() { + switch (_direction) { + case DIR_UP: + _y--; break; + case DIR_DOWN: + _y++; break; + case DIR_LEFT: + _x--; break; + case DIR_RIGHT: + _x++; break; + } + if (_x < 0 || _x >= _width || _y < 0 || _y >= _height || + FIELD(_x, _y) == FIELD_SNAKE) { + // Snek ded + qDebug() << "DEATH"; + memset(_field, 0, sizeof(*_field) * _width * _height); + _x = _width / 2; + _y = _height / 2; + _snake.clear(); + _widget->update(); + _snakeLen = 5; + addFood(); + return; + } + // Normal + if (_snake.size() >= _snakeLen) { + const QPoint &p = _snake.front(); + if (FIELD(p.x(), p.y()) == FIELD_SNAKE) { + FIELD(p.x(), p.y()) = FIELD_FREE; + _widget->update(p.x() * SCALING, p.y() * SCALING, SCALING, SCALING); + } + _snake.pop_front(); + } + if (FIELD(_x, _y) == FIELD_FOOD) { + if (qrand() % _snakeLen > 40) { + _snakeLen = 10; + while (_snake.size() > _snakeLen) { + _snake.pop_front(); + } + addFood(); + } else { + _snakeLen += 5; + } + addFood(); + } + FIELD(_x, _y) = FIELD_SNAKE; + _snake.append(QPoint(_x, _y)); + _widget->update(_x * SCALING, _y * SCALING, SCALING, SCALING); + // AI + int what[4], dist[4]; + int best = 0; + scanDir(0, -1, what[DIR_UP], dist[DIR_UP]); + scanDir(0, 1, what[DIR_DOWN], dist[DIR_DOWN]); + scanDir(-1, 0, what[DIR_LEFT], dist[DIR_LEFT]); + scanDir(1, 0, what[DIR_RIGHT], dist[DIR_RIGHT]); + for (int i = 1; i < 4; ++i) { + if (what[i] == FIELD_FOOD) { + if (what[best] != FIELD_FOOD || dist[best] > dist[i]) { + best = i; + } + } else if (what[best] != FIELD_FOOD && dist[i] > dist[best]) { + best = i; + } + } + if (what[best] == FIELD_FOOD) { + _direction = best; + } else if (dist[_direction] <= 1) { + _direction = best; + } + }); +} + +Snake::~Snake() +{ + free(_field); +} + +void Snake::scanDir(int dx, int dy, int &what, int &dist) +{ + int x = _x; + int y = _y; + dist = 0; + for (;;) { + x += dx; + y += dy; + dist++; + if (x < 0 || x >= _width || y < 0 || y >= _height) { + what = FIELD_SNAKE; + return; + } + if (FIELD(x, y) != FIELD_FREE) { + what = FIELD(x, y); + return; + } + } +} + +void Snake::addFood() +{ + for (int i = 0; i < 10; ++i) { + int x = qrand() % _width; + int y = qrand() % _height; + if (FIELD(x, y) != FIELD_FOOD) { + FIELD(x, y) = FIELD_FOOD; + _widget->update(x * SCALING, y * SCALING, SCALING, SCALING); + break; + } + } +} + +void Snake::paint(QPaintEvent *event) +{ + QPainter p(_widget); + const int width = qMin(_width, (event->rect().x() + event->rect().width() + SCALING - 1) / SCALING); + const int height = qMin(_height, (event->rect().y() + event->rect().height() + SCALING - 1) / SCALING); + for (int y = event->rect().y() / SCALING; y < height; ++y) { + if (y < 0) + continue; + for (int x = event->rect().x() / SCALING; x < width; ++x) { + if (x < 0) + continue; + switch (FIELD(x, y)) { + case FIELD_FOOD: + p.setBrush(Qt::green); + break; + case FIELD_SNAKE: + p.setBrush(Qt::white); + break; + default: + continue; + } + p.drawRect(x * SCALING, y * SCALING, SCALING-1, SCALING-1); + } + } +} |