AI improvements
Weights adjustments. Improved data collection performances to speed-up decisions.
This commit is contained in:
parent
a8614eb4ff
commit
9d4a57d98e
|
@ -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 ) {
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue