ساختار Agent2D ( قسمت اول ) :
به طور عمده فایل های موجود در Agent2D متشکل از دو نوع هستند، فایل هایی که با پیشوند role آمده اند و فایل هایی که با پیشوند bhv آمده اند. role ها در واقع همان سیستم تصمیم گیری ای است که به صورت پیشفرض در آن مد نظر گرفته شده است.
- Role ها :
در Agent2D ، شش نوع role کاربردی وجود دارد که که عبارتند از :
1. role_center_back : مربوط به دو بازیکن دفاعی ( بازیکن 3 و 4 ) که نقش دفاعهای وسط را بر عهده داند.
2.role_side_back : مربوط به بازیکنان دفاع که در گوشه قرار دارند ( بازکنان 2 و 5 ).
3.role_defensive_half : مربوط به بازیکن شماره ی 6 که هافبک وسط میباشد.
4.role_offensive_half : مربوط به بازیکنان هافبک که در گوشه قرار دارند ( 7 و 8 ).
5.role_side_forward : مربوط به بازیکنان گوشه ی حمله ، یا به اصطلاح بازیکنان بال حمله، ( بازیکنان 11 , 10 )
6.role_center_forward : مربوط به بازیکن نوک حمله، یعنی بازیکن شماره ی 9 که در وسط بالها قرار داند.
تمامی فایلهای role ها متشکل از 3 قسمت هستند. که عبارتند از :
1. execute : این تابع که در حقیقت تابع اصلی فایل های role می باشد، متشکل از یک سیستم ساده است ،
void
RoleSample::execute( rcsc::PlayerAgent * agent )
{
bool kickable = agent->world().self().isKickable();
if ( agent->world().existKickableTeammate()
&& agent->world().teammatesFromBall().front()->distFromBall()
< agent->world().ball().distFromSelf() )
{
kickable = false;
}
if ( kickable )
{
doKick( agent );
}
else
{
doMove( agent );
}
}
همانطور که مشاهده میکنید، این تابع ، تابع مادر فایل است، که با یک if اصلی مشخص میکند که آیا توپ kickable هست یا خیر. در کد زیر شرطی که چک میشود بدین صورت است :
if ( agent->world().existKickableTeammate()
&& agent->world().teammatesFromBall().front()->distFromBall()
< agent->world().ball().distFromSelf() )
اگر این شرط درست باشد یعنی توپ قابل ضربه بود، متغیر kickable دارای مقدار true و اگر نبود، دارای مقدار false می شود.
سپس با توجه به kickable بودن توپ دو تابع وابسته یعنی doMove و doKick در تابع execute فراخوانی میشود.
if ( kickable ) /// if ball is kickable call doKick
{
doKick( agent );
}
else ///and if ball is kickable call doMove, it called in else
{
doMove( agent );
}
2. تابع doKick : تابع doKick زمانی به کار میرود که توپ kickable باشد، یعنی قابل ضربه زدن برای عامل باشد.
اگر در این تابع دقت کنید متوجه یک switch میشوید که در آن به کار رفته است،
void
RoleSample::doKick( rcsc::PlayerAgent * agent )
{
switch ( Strategy::get_ball_area( agent->world().ball().pos() ) ) {
case Strategy::BA_CrossBlock:
case Strategy::BA_Stopper:
case Strategy::BA_Danger:
case Strategy::BA_DribbleBlock:
case Strategy::BA_DefMidField:
case Strategy::BA_DribbleAttack:
case Strategy::BA_OffMidField:
case Strategy::BA_Cross:
case Strategy::BA_ShootChance:
default:
Bhv_BasicOffensiveKick().execute( agent );
break;
}
}
برای درک بهتر نیاز است که سری به فایل strategy.cpp بزنیم، اگر در strategy.cpp دقت کنید تابعی در آن وجود دارد به نام get_ball_area که یک ورودی از نوع Vector2D میگیرد.
Strategy::BallArea
Strategy::get_ball_area( const rcsc::Vector2D & ball_pos )
{
if ( ball_pos.x > 36.0 )
{
if ( ball_pos.absY() > 17.0 )
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: Cross" );
return BA_Cross;
}
else
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: ShootChance" );
return BA_ShootChance;
}
}
else if ( ball_pos.x > -1.0 )
{
if ( ball_pos.absY() > 17.0 )
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: DribbleAttack" );
return BA_DribbleAttack;
}
else
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: OffMidField" );
return BA_OffMidField;
}
}
else if ( ball_pos.x > -30.0 )
{
if ( ball_pos.absY() > 17.0 )
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: DribbleBlock" );
return BA_DribbleBlock;
}
else
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: DefMidField" );
return BA_DefMidField;
}
}
else if ( ball_pos.x > -36.5 )
{
if ( ball_pos.absY() > 17.0 )
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: CrossBlock" );
return BA_CrossBlock;
}
else
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: Stopper" );
return BA_Stopper;
}
}
else
{
if ( ball_pos.absY() > 17.0 )
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: CrossBlock" );
return BA_CrossBlock;
}
else
{
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: Danger" );
return BA_Danger;
}
}
rcsc::dlog.addText( rcsc::Logger::TEAM,
__FILE__": get_ball_area: unknown area" );
return BA_None;
}
تابع get_ball_area در حقیقت یک سیستم قسمت بندی زمین است که با توجه به هر قسمت در زمین، یک نام از enum ای که نامش BallArea میباشد، به آن اختصاص داده می شود. در حقیقت خروجی این تابع نام قسمتی است که توپ ( ورودی ) در آن قرار دارد.
همانطور که مشخص است ورودی این تابع در doKick مختصات توپ است، و با توجه به هر قسمت زمین که در یک case قرار گرفته شده است، دستوراتی که مختص آن ناحیه ایت ، توسط عامل انجام می شود.
3. تابع doMove : این تابع ازاحاط ساختاری کاملا همانند تابع doKick میباشد، با این تفاوت که این تابع زمانی فراخوانی میشود که توپ قابل ظربه زدن نباشد ( false == kickable ) باشد. این تابع به نوعی positioning ( موقعیت یابی ) عامل را در حین بازی مشخص میکند ( با استفاده از دستورات مختص هر ناحیه از زمین ).
void
RoleSample::doMove( rcsc::PlayerAgent * agent )
{
rcsc::Vector2D home_pos = Strategy::i().getPosition( agent->world().self().unum() );
if ( ! home_pos.valid() ) home_pos.assign( 0.0, 0.0 );
switch ( Strategy::get_ball_area( agent->world() ) ) {
case Strategy::BA_CrossBlock:
case Strategy::BA_Stopper:
case Strategy::BA_Danger:
case Strategy::BA_DribbleBlock:
case Strategy::BA_DefMidField:
case Strategy::BA_DribbleAttack:
case Strategy::BA_OffMidField:
case Strategy::BA_Cross:
case Strategy::BA_ShootChance:
default:
Bhv_BasicMove( home_pos ).execute( agent );
break;
}
}