AI improvements

Weights adjustments.
Improved data collection performances to speed-up decisions.
This commit is contained in:
Valentino Orlandi 2022-12-01 18:33:55 +01:00
parent a8614eb4ff
commit 9d4a57d98e
Signed by: elB4RTO
GPG Key ID: 1719E976DB2D4E71
2 changed files with 155 additions and 90 deletions

View File

@ -4,8 +4,17 @@
Snake::Snake( const bool& is_adversary )
{
this->adversary = is_adversary;
this->will_grow = false;
this->adversary = is_adversary;
if ( is_adversary ) {
for ( size_t x=0; x<16; x++ ) {
this->field_map.push_back( std::vector<Tile>{} );
std::vector<Tile>& v = this->field_map.back();
for ( size_t y=0; y<16; y++ ) {
v.push_back({ Entity::N, 0 });
}
}
}
}
@ -42,26 +51,6 @@ const bool Snake::inTile( const unsigned int& x, const unsigned int& y , const b
return result;
}
const bool Snake::inTileMinusSteps(const unsigned int& x, const unsigned int& y, const unsigned int& steps )
{
bool result = false;
if ( this->size() > 0 ) {
unsigned int
i = 1,
size = this->size();
for ( std::vector<BodyPart>::const_iterator bp = this->begin(); bp != this->end(); ++bp ) {if ( bp->x == x && bp->y == y ) {
result = true;
break;
}
i ++;
if ( i >= size-steps ) {
break;
}
}
}
return result;
}
void Snake::setDirection( const Direction new_direction )
{
@ -378,7 +367,7 @@ void Snake::update( QGraphicsScene* field_scene, const bool& dry , const bool& i
}
void Snake::move( Snake& snake, const unsigned int& food_x, const unsigned int& food_y )
void Snake::move( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y )
{
std::vector<Direction> classes = {
Direction::UP,
@ -400,12 +389,14 @@ void Snake::move( Snake& snake, const unsigned int& food_x, const unsigned int&
0.003, // dead way steps
0.1, // food way
0.4, // aggressive way
0.05, // same direction
0.01, // same direction
-1.0 // opposite direction
};
this->updateFieldMap( adv_snake, food_x, food_y );
for ( int i=0; i<4; i++ ) {
this->collectData( dataset.at(i), classes.at(i), snake, food_x, food_y );
this->collectData( dataset.at(i), classes.at(i), adv_snake, food_x, food_y );
}
// decide
@ -549,17 +540,18 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
if ( !(blocked_way || opposite_direction) ) {
// check other snakes
if ( this->inTile( x, y ) ) {
blocked_way = 1;
} else if ( adv_snake.inTile( x, y ) ) {
blocked_way = 1;
// check snakes
switch ( this->field_map.at( x ).at( y ).entity ) {
case Entity::S:
case Entity::A:
blocked_way = 1;
break;
}
if ( ! blocked_way ) {
// check for deadhole
dead_way_steps = this->isDeadHole( x, y, direction, adv_snake );
dead_way_steps = this->isDeadHole( x, y, direction );
if ( dead_way_steps > 0 ) {
dead_way = 1;
}
@ -569,16 +561,16 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
case Direction::UP:
if ( adv_snake.direction() == Direction::LEFT && this->head_direction == Direction::RIGHT ) {
if ( adv_snake.inTile( x+2, y+1 ) ) {
if ( this->inTileAdv( x+2, y+1 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == Direction::RIGHT && this->head_direction == Direction::LEFT ) {
if ( adv_snake.inTile( x-2, y+1 ) ) {
if ( this->inTileAdv( x-2, y+1 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == this->head_direction ) {
if ( adv_snake.inTile( x+2, y )
|| adv_snake.inTile( x-2, y ) ) {
if ( this->inTileAdv( x+2, y )
|| this->inTileAdv( x-2, y ) ) {
if ( !rand()%this->aggressiveness ) {
aggressive_way = 1;
}
@ -588,16 +580,16 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
case Direction::DOWN:
if ( adv_snake.direction() == Direction::LEFT && this->head_direction == Direction::RIGHT ) {
if ( adv_snake.inTile( x+2, y-1 ) ) {
if ( this->inTileAdv( x+2, y-1 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == Direction::RIGHT && this->head_direction == Direction::LEFT ) {
if ( adv_snake.inTile( x-2, y-1 ) ) {
if ( this->inTileAdv( x-2, y-1 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == this->head_direction ) {
if ( adv_snake.inTile( x+2, y )
|| adv_snake.inTile( x-2, y ) ) {
if ( this->inTileAdv( x+2, y )
|| this->inTileAdv( x-2, y ) ) {
if ( !rand()%this->aggressiveness ) {
aggressive_way = 1;
}
@ -607,16 +599,16 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
case Direction::LEFT:
if ( adv_snake.direction() == Direction::UP && this->head_direction == Direction::DOWN ) {
if ( adv_snake.inTile( x+1, y+2 ) ) {
if ( this->inTileAdv( x+1, y+2 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == Direction::DOWN && this->head_direction == Direction::UP ) {
if ( adv_snake.inTile( x+1, y-2 ) ) {
if ( this->inTileAdv( x+1, y-2 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == this->head_direction ) {
if ( adv_snake.inTile( x, y+2 )
|| adv_snake.inTile( x, y-2 ) ) {
if ( this->inTileAdv( x, y+2 )
|| this->inTileAdv( x, y-2 ) ) {
if ( !rand()%this->aggressiveness ) {
aggressive_way = 1;
}
@ -626,16 +618,16 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
case Direction::RIGHT:
if ( adv_snake.direction() == Direction::UP && this->head_direction == Direction::DOWN ) {
if ( adv_snake.inTile( x-1, y+2 ) ) {
if ( this->inTileAdv( x-1, y+2 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == Direction::DOWN && this->head_direction == Direction::UP ) {
if ( adv_snake.inTile( x-1, y-2 ) ) {
if ( this->inTileAdv( x-1, y-2 ) ) {
aggressive_way = 1;
}
} else if ( adv_snake.direction() == this->head_direction ) {
if ( adv_snake.inTile( x, y+2 )
|| adv_snake.inTile( x, y-2 ) ) {
if ( this->inTileAdv( x, y+2 )
|| this->inTileAdv( x, y-2 ) ) {
if ( !rand()%this->aggressiveness ) {
aggressive_way = 1;
}
@ -730,7 +722,66 @@ void Snake::collectData( std::vector<float>& data, Direction& direction, Snake&
}
const std::vector<unsigned int> Snake::checkAround( const Direction& direction, Snake& adv_snake, const unsigned int& x, const unsigned int& y )
void Snake::updateFieldMap( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y )
{
// reset to default state
for ( size_t x=0; x<16; x++ ) {
for ( size_t y=0; y<16; y++ ) {
Tile& t = this->field_map.at(x).at(y);
t.entity = Entity::N;
t.s_index = 0;
}
}
// update food position
this->field_map.at(food_x).at(food_y).entity = Entity::F;
// update self
unsigned int i = this->size();
for ( std::vector<BodyPart>::const_iterator bp = this->begin(); bp != this->end(); ++bp ) {
Tile& t = this->field_map.at(bp->x).at(bp->y);
t.entity = Entity::S;
t.s_index = i;
i--;
}
// update adversary
i = adv_snake.size();
for ( const auto& bp : adv_snake ) {
Tile& t = this->field_map.at(bp.x).at(bp.y);
t.entity = Entity::A;
t.s_index = i;
i--;
}
}
const bool Snake::inTileAdv(const unsigned int& x, const unsigned int& y )
{
bool result = false;
if ( x < 16 && y < 16 ) {
switch ( this->field_map.at(x).at(y).entity ) {
case Entity::A:
result = true;
break;
}
}
return result;
}
const bool Snake::inTileMinusSteps(const unsigned int& x, const unsigned int& y, const unsigned int& steps )
{
bool result = false;
switch ( this->field_map.at(x).at(y).entity ) {
case Entity::S:
case Entity::A:
if ( this->field_map.at(x).at(y).s_index > steps ) {
result = true;
}
break;
}
return result;
}
const std::vector<unsigned int> Snake::checkAround( const Direction& direction, const unsigned int& x, const unsigned int& y )
{
std::vector<unsigned int> around = {
0, 0, 0,
@ -786,10 +837,13 @@ const std::vector<unsigned int> Snake::checkAround( const Direction& direction,
y_ += y_pattern.at(i);
if ( x_ > 15 || y_ > 15 ) {
around.at(i) = 1;
} else if ( this->inTile( x_, y_ ) ) {
around.at(i) = 2;
} else if ( adv_snake.inTile( x_, y_ ) ) {
around.at(i) = 2;
} else {
switch ( this->field_map.at( x_ ).at( y_ ).entity ) {
case Entity::S:
case Entity::A:
around.at(i) = 2;
break;
}
}
}
@ -797,14 +851,14 @@ const std::vector<unsigned int> Snake::checkAround( const Direction& direction,
}
const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction, Snake& adv_snake , const bool& inverse )
const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction )
{
bool result=false, check=false, check_clockwise=false;
int front_step, front_check, side_check;
unsigned int steps=1, side, front;
Direction direction = start_direction;
const auto blocked_around = this->checkAround( direction, adv_snake, start_x, start_y );
const auto blocked_around = this->checkAround( direction, start_x, start_y );
if ( (blocked_around.at(3)>0 && blocked_around.at(4)>0)
&& (blocked_around.at(3)>1 || blocked_around.at(4)>1) ) {
check = true;
@ -815,11 +869,11 @@ const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigne
&& (blocked_around.at(4)>1 || blocked_around.at(5)>1) ) {
check = true;
check_clockwise = true;
} else if ( blocked_around.at(0)>0 && blocked_around.at(3)>0 ) {
} else if ( (blocked_around.at(5)>0 && blocked_around.at(7)>0)
&& (blocked_around.at(5)>1 || blocked_around.at(7)>1) ) {
check = true;
} else if ( blocked_around.at(2)>0 && blocked_around.at(4)>0 ) {
} else if ( blocked_around.at(0)>0 && blocked_around.at(1)>0 && blocked_around.at(2)>0 ) {
check = true;
check_clockwise = true;
}
@ -952,15 +1006,11 @@ const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigne
}
if ( x > 15 || y > 15 ) {
blocked = true;
} else if ( adv_snake.inTileMinusSteps( x, y, steps ) ) {
blocked = true;
} else if ( this->inTileMinusSteps( x, y, steps ) ) {
blocked = true;
} else {
if ( tried_already( side_, front_, false ) ) {
if ( this->inTile( x_, y_ ) ) {
blocked = true;
}
} else if ( tried_already( side_, front_, false ) ) {
if ( this->field_map.at( x_ ).at( y_ ).entity == Entity::S ) {
blocked = true;
}
}
return blocked;
@ -1063,30 +1113,25 @@ const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigne
unsigned int aux_steps=0, i=0;
while ( true ) {
check_deadhole();
i ++;
if ( i >= 2 || !result ) {
break;
} else {
aux_result = result;
result = false;
aux_steps = steps;
steps = 1;
check_clockwise = !check_clockwise;
direction = start_direction;
tried_positions.clear();
if ( !result ) {
steps = 0;
}
i ++;
if ( i == 2 ) {
break;
}
aux_result = result;
result = false;
aux_steps = steps;
steps = 1;
check_clockwise = !check_clockwise;
direction = start_direction;
tried_positions.clear();
}
// choose the best result (namely, the negative one or the positive one with more steps)
if ( result ) {
if ( aux_result ) {
if ( aux_steps > steps ) {
steps = aux_steps;
}
} else {
result = aux_result;
}
}
// mean result
result = result | aux_result;
steps = (steps>aux_steps) ? steps : aux_steps;
}
if ( !result ) {

View File

@ -45,9 +45,6 @@ public:
//! Checks whether is there a part of the snake in the given position
const bool inTile( const unsigned int& x, const unsigned int& y, const bool& avoid_tail=true );
// [AI] As inTile(), without counting as much trailing BodyParts as the number of steps
const bool inTileMinusSteps( const unsigned int& x, const unsigned int& y, const unsigned int& steps );
//! Sets the new direction (of the head)
void setDirection( const Direction new_direction );
@ -61,7 +58,7 @@ public:
void willGrow();
// [AI] Chooses a new direction for the snake
void move( Snake& snake, const unsigned int& food_x, const unsigned int& food_y );
void move( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );
private:
@ -89,11 +86,34 @@ private:
const unsigned int aggressiveness = 10 - (rand()%9);
enum Entity {
N, // none
S, // self
A, // adversary
F, // food
};
struct Tile {
Entity entity;
unsigned int s_index;
};
std::vector<std::vector<Tile>> field_map;
// [AI] Updates the map of the field
void updateFieldMap( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );
// [AI] As inTile(), but works on the field_map and only checks the adersary
const bool inTileAdv( const unsigned int& x, const unsigned int& y );
// [AI] Checks whether is there a snake in the tile, without counting as much trailing BodyParts as the number of steps
const bool inTileMinusSteps( const unsigned int& x, const unsigned int& y, const unsigned int& steps );
// [AI] Checks which of the surrounding positions are blocked
const std::vector<unsigned int> checkAround( const Direction& direction, Snake& adv_snake, const unsigned int& x, const unsigned int& y );
const std::vector<unsigned int> checkAround( const Direction& direction, const unsigned int& x, const unsigned int& y );
// [AI] Checks if a direction is a closed path and should be avoided
const unsigned int isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction, Snake& adv_snake, const bool& inverse=false );
const unsigned int isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction );
// [AI] Collects data about the possible movements
void collectData( std::vector<float>& data, Direction& direction, Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );