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