Snake effect now uses a neural net with a hidden layer; a bug in _is_free() that lead to lots of snakes committing suicide was fixed; neural net weights are now given as binary representation of a float to prevent rounding errors.
This commit is contained in:
@ -10,6 +10,7 @@ SnakeEffect::SnakeEffect() {
|
||||
|
||||
void SnakeEffect::_init() {
|
||||
_dying = 0;
|
||||
_round = 0;
|
||||
_last_apple_at = millis();
|
||||
_last_move_at = millis();
|
||||
_dir = SNAKE_DIR_NORTH;
|
||||
@ -17,6 +18,9 @@ void SnakeEffect::_init() {
|
||||
_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;
|
||||
_map[_xy2i(_pos)+window->width*1]=2;
|
||||
_map[_xy2i(_pos)+window->width*2]=3;
|
||||
_map[_xy2i(_pos)+window->width*3]=4;
|
||||
_place_apple();
|
||||
}
|
||||
|
||||
@ -26,6 +30,10 @@ SnakeEffect::~SnakeEffect() {
|
||||
}
|
||||
|
||||
void SnakeEffect::_place_apple() {
|
||||
if (SNAKE_DEBUG) {
|
||||
_apple = {3, 3};
|
||||
return;
|
||||
}
|
||||
if (_length < _pixels) {
|
||||
uint8_t start = random8(_pixels);
|
||||
for (int i=0; i<_pixels; i++) {
|
||||
@ -45,27 +53,50 @@ void SnakeEffect::_decide() {
|
||||
uint8_t a_s = _to_apple(_dir);
|
||||
uint8_t a_r = _to_apple(_dir + 1);
|
||||
|
||||
uint8_t inputs[6] = {f_l, f_s, f_r, a_l, a_s, a_r};
|
||||
|
||||
float outputs[3] = {0.0, 0.0, 0.0};
|
||||
|
||||
for (int i=0; i<18; i++) {
|
||||
uint8_t out = i/6;
|
||||
uint8_t in = i%6;
|
||||
outputs[out] += _weights[i] * inputs[in];
|
||||
float* inputs = new float[6];
|
||||
inputs[0] = f_l;
|
||||
inputs[1] = f_s;
|
||||
inputs[2] = f_r;
|
||||
inputs[3] = a_l;
|
||||
inputs[4] = a_s;
|
||||
inputs[5] = a_r;
|
||||
if (SNAKE_DEBUG) LOGln("SnakeEffect * Position: %d, %d - Inputs: %3.1f %3.1f %3.1f %3.1f %3.1f %3.1f", _pos.x, _pos.y, inputs[0], inputs[1], inputs[2], inputs[3], inputs[4], inputs[5]);
|
||||
float* outputs = NULL;
|
||||
uint8_t i=0;
|
||||
for (uint8_t layer=1; layer<_net_layers; layer++) {
|
||||
outputs = new float[_net_layout[layer]];
|
||||
for (uint8_t j=0; j<_net_layout[layer]; j++) {
|
||||
outputs[j] = 0.0;
|
||||
}
|
||||
for (uint8_t idx_out=0; idx_out<_net_layout[layer]; idx_out++) {
|
||||
for (uint8_t idx_in=0; idx_in<_net_layout[layer-1]; idx_in++) {
|
||||
float weight;
|
||||
memcpy(&weight, &(_weights[i]), sizeof(weight));
|
||||
outputs[idx_out] += weight * inputs[idx_in];
|
||||
//outputs[idx_out] += (*(float*)&(_weights[i])) * inputs[idx_in];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
delete inputs;
|
||||
inputs = outputs;
|
||||
}
|
||||
|
||||
int8_t decision = 0;
|
||||
if (outputs[0]>=outputs[1] && outputs[0]>=outputs[2]) {
|
||||
decision = -1;
|
||||
} else if (outputs[1]>=outputs[2]) {
|
||||
decision = 0;
|
||||
} else {
|
||||
decision = 1;
|
||||
float last;
|
||||
for (uint8_t i=0; i<_net_layout[_net_layers - 1]; i++) {
|
||||
if (i==0 || outputs[i]>last) {
|
||||
last = outputs[i];
|
||||
decision = i;
|
||||
}
|
||||
}
|
||||
decision = decision - 1;
|
||||
delete outputs;
|
||||
|
||||
if (SNAKE_DEBUG) LOGln("SnakeEffect * Decision: %d", decision);
|
||||
|
||||
_dir += decision;
|
||||
if (_dir < 0) _dir += 4;
|
||||
if (_dir > 3) _dir -= 4;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +125,7 @@ int8_t SnakeEffect::_manual_decision() {
|
||||
}*/
|
||||
|
||||
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;
|
||||
return _free_spaces(dir)!=0;
|
||||
}
|
||||
|
||||
uint8_t SnakeEffect::_free_spaces(uint8_t dir) {
|
||||
@ -109,15 +139,15 @@ uint8_t SnakeEffect::_free_spaces(uint8_t dir) {
|
||||
case SNAKE_DIR_WEST: x=-1; break;
|
||||
}
|
||||
Coords p(_pos);
|
||||
uint8_t i;
|
||||
for(i=0; i<window->width || i<window->height; i++) {
|
||||
uint8_t i=0;
|
||||
while (true) {
|
||||
p.x += x;
|
||||
p.y += y;
|
||||
if (p.x<0 || p.x>=window->width || p.y<0 || p.y>=window->height || _map[_xy2i(p)]!=0) {
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
uint8_t SnakeEffect::_to_apple(uint8_t dir) {
|
||||
@ -159,10 +189,7 @@ Coords SnakeEffect::_i2xy(uint16_t i) {
|
||||
}
|
||||
|
||||
void SnakeEffect::_move() {
|
||||
if (_dying==0 && !_is_free(_dir)) {
|
||||
_dying = 150;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_dying > 0) {
|
||||
_dying--;
|
||||
@ -175,11 +202,21 @@ void SnakeEffect::_move() {
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
if (_last_move_at < now && now - _last_move_at < 100) {
|
||||
if (_last_move_at < now && now - _last_move_at < 0) {
|
||||
return;
|
||||
}
|
||||
_round++;
|
||||
_last_move_at = now;
|
||||
_decide();
|
||||
|
||||
if (_dying==0 && !_is_free(_dir)) {
|
||||
_dying = 150;
|
||||
return;
|
||||
}
|
||||
|
||||
_pos = _new_pos(_dir);
|
||||
if (SNAKE_DEBUG) LOGln("SnakeEffect * new_pos: %d, %d", _pos.x, _pos.y);
|
||||
if (SNAKE_DEBUG) LOGln("SnakeEffect * apple: %d, %d", _apple.x, _apple.y);
|
||||
if (_pos.x==_apple.x && _pos.y==_apple.y) {
|
||||
_last_apple_at = millis();
|
||||
_length++;
|
||||
@ -215,12 +252,9 @@ void SnakeEffect::loop(uint16_t ms) {
|
||||
//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) {
|
||||
if (_dying==0 && (millis() < _last_apple_at || millis() - _last_apple_at > 30000)) {
|
||||
_dying = 150;
|
||||
}
|
||||
if (_dying==0) {
|
||||
_decide();
|
||||
}
|
||||
_move();
|
||||
_draw();
|
||||
}
|
||||
|
Reference in New Issue
Block a user