summaryrefslogtreecommitdiffstats
path: root/src/snake.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/snake.cpp')
-rw-r--r--src/snake.cpp172
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);
+ }
+ }
+}