#include "snake.h" #include #include #include #include #include #include #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); } } }