pitrix/src/effect_snake.cpp

186 lines
4.0 KiB
C++

#include "effect_snake.h"
#include "functions.h"
SnakeEffect::SnakeEffect() {
window = new Window(0, 0, LED_WIDTH, LED_HEIGHT-6);
_pixels = window->width * window->height;
_map = new uint8_t[_pixels];
_init();
}
void SnakeEffect::_init() {
_dying = 0;
_last_apple_at = millis();
_last_move_at = millis();
_dir = SNAKE_DIR_NORTH;
_length = 4;
_pos = {(uint8_t)(window->width/2), (uint8_t)(window->height/2)};
for (int i=0; i<_pixels; i++) _map[i]=0;
_map[_xy2i(_pos)]=1;
_place_apple();
}
SnakeEffect::~SnakeEffect() {
delete window;
delete _map;
}
void SnakeEffect::_place_apple() {
if (_length < _pixels) {
uint8_t start = random8(_pixels);
for (int i=0; i<_pixels; i++) {
if (_map[start + i]==0) {
_apple = _i2xy(start + i);
return;
}
}
}
}
void SnakeEffect::_decide() {
uint8_t input = 0;
if (_is_free(_dir - 1)) input |= 1<<5;
if (_is_free(_dir)) input |= 1<<4;
if (_is_free(_dir + 1)) input |= 1<<3;
if (_to_apple(_dir - 1)) input |= 1<<2;
if (_to_apple(_dir)) input |= 1<<1;
if (_to_apple(_dir + 1)) input |= 1;
_dir += _decisions[input];
if (_dir < 0) _dir += 4;
}
/**
* This is old (and hence disabled) code, showing
* a simple, hand-crafted "AI" for plaing snake.
**
int8_t SnakeEffect::_manual_decision() {
bool free_l = _is_free(_dir - 1);
bool free_s = _is_free(_dir);
bool free_r = _is_free(_dir + 1);
bool apple_l = _to_apple(_dir - 1);
bool apple_s = _to_apple(_dir);
bool apple_r = _to_apple(_dir + 1);
if (!free_s) {
if (apple_l && free_l) return -1;
if (apple_r && free_r) return 1;
if (free_l) return -1;
return 1;
}
if (apple_s) return 0;
if (apple_l && free_l) return -1;
if (apple_r && free_r) return 1;
return 0;
}*/
bool SnakeEffect::_is_free(uint8_t dir) {
Coords np = _new_pos(dir);
return np.x>=0 && np.x<window->width && np.y>=0 && np.y<window->height && _map[_xy2i(np)]==0;
}
bool SnakeEffect::_to_apple(uint8_t dir) {
uint8_t d = dir % 4;
switch(d) {
case SNAKE_DIR_NORTH: return _apple.y<_pos.y;
case SNAKE_DIR_EAST: return _apple.x>_pos.x;
case SNAKE_DIR_SOUTH: return _apple.y>_pos.y;
case SNAKE_DIR_WEST: return _apple.x<_pos.x;
}
return true;
}
Coords SnakeEffect::_new_pos(uint8_t dir) {
uint8_t d = dir % 4;
Coords p(_pos);
switch(d) {
case SNAKE_DIR_NORTH: p.y--; break;
case SNAKE_DIR_EAST: p.x++; break;
case SNAKE_DIR_SOUTH: p.y++; break;
case SNAKE_DIR_WEST: p.x--; break;
}
return p;
}
uint16_t SnakeEffect::_xy2i(Coords c) {
return _xy2i(c.x, c.y);
}
uint16_t SnakeEffect::_xy2i(uint8_t x, uint8_t y) {
return y*window->width + x;
}
Coords SnakeEffect::_i2xy(uint16_t i) {
return {(uint16_t)(i%window->width), (uint16_t)(i/window->width)};
}
void SnakeEffect::_move() {
if (_dying==0 && !_is_free(_dir)) {
_dying = 150;
return;
}
if (_dying > 0) {
_dying--;
if (_dying==0) {
_init();
}
return;
}
unsigned long now = millis();
if (_last_move_at < now && now - _last_move_at < 100) {
return;
}
_last_move_at = now;
_pos = _new_pos(_dir);
if (_pos.x==_apple.x && _pos.y==_apple.y) {
_last_apple_at = millis();
_length++;
}
for (int i=0; i<_pixels; i++) {
if (_map[i]>0 && _map[i]<_length-1) _map[i]++;
else _map[i]=0;
}
_map[_xy2i(_pos)] = 1;
if (_pos.x==_apple.x && _pos.y==_apple.y) {
_place_apple();
}
}
void SnakeEffect::_draw() {
if (_dying) {
window->fadeToBlackBy(4);
return;
}
window->clear();
CRGB red(0xBB0000);
for (int i=0; i<_pixels; i++) {
if (_map[i]>0) window->setPixelByIndex(i, &red);
}
CRGB white(0xFFFFFF);
window->setPixel(_pos.x, _pos.y, &white);
CRGB green(0xFFFF00);
window->setPixel(_apple.x, _apple.y, &green);
}
void SnakeEffect::loop(uint16_t ms) {
//window->fadeToBlackBy(2);
//CRGB color(CHSV(hue, 200, 255));
//window->setPixel(this->coords.x, this->coords.y, &color);
//hue++;
if (millis() < _last_apple_at || millis() - _last_apple_at > 30000) {
_dying = 150;
}
if (_dying==0) {
_decide();
}
_move();
_draw();
}
boolean SnakeEffect::can_be_shown_with_clock() { return true; }