summaryrefslogblamecommitdiffstats
path: root/src/snake.cpp
blob: 6f1867016258db502a45647061fb6e02b5ba4961 (plain) (tree)











































































































































































                                                                                                           
#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);
        }
    }
}