• Robcup2D足球学习记录【2020.01.14】


    bhv_normal_dribble.cpp

    代码个人理解与存在的问题(已经通过注释标出)

    注释的格式为:
    /*
    问题:
    1.xxxx
    2.xxxx

    理解:
    这个函数是xxxxx
    By ChenYanTing
    */

    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
    
    #include "bhv_normal_dribble.h"
    
    #include "action_chain_holder.h"
    #include "action_chain_graph.h"
    #include "cooperative_action.h"
    
    #include "dribble.h"
    #include "short_dribble_generator.h"
    
    #include <rcsc/action/basic_actions.h>
    #include <rcsc/action/neck_scan_field.h>
    #include <rcsc/action/neck_turn_to_ball_or_scan.h>
    #include <rcsc/action/view_synch.h>
    
    #include <rcsc/player/intercept_table.h>
    #include <rcsc/player/player_agent.h>
    #include <rcsc/player/soccer_intention.h>
    #include <rcsc/player/debug_client.h>
    #include <rcsc/common/logger.h>
    #include <rcsc/common/audio_memory.h>
    #include <rcsc/common/server_param.h>
    #include <rcsc/soccer_math.h>
    #include <rcsc/math_util.h>
    
    //#define DEBUG_PRINT
    
    using namespace rcsc;
    
    class IntentionNormalDribble
        : public SoccerIntention {
    private:
        const Vector2D M_target_point; //!< trapped ball position
    
        int M_turn_step; //!< remained turn step
        int M_dash_step; //!< remained dash step
    
        GameTime M_last_execute_time; //!< last executed time
    
        NeckAction::Ptr M_neck_action;
        ViewAction::Ptr M_view_action;
    
    public:
    
        IntentionNormalDribble( const Vector2D & target_point,
                                const int n_turn,
                                const int n_dash,
                                const GameTime & start_time ,
                                NeckAction::Ptr neck = NeckAction::Ptr(),
                                ViewAction::Ptr view = ViewAction::Ptr() )
            : M_target_point( target_point ),
              M_turn_step( n_turn ),
              M_dash_step( n_dash ),
              M_last_execute_time( start_time ),
              M_neck_action( neck ),
              M_view_action( view )
          { }
    
        bool finished( const PlayerAgent * agent );
    
        bool execute( PlayerAgent * agent );
    
    private:
        /*!
          rief clear the action queue
         */
        void clear()
          {
              M_turn_step = M_dash_step = 0;
          }
    
        bool checkOpponent( const WorldModel & wm );
        bool doTurn( PlayerAgent * agent );
        bool doDash( PlayerAgent * agent );
    };
    
    /*-------------------------------------------------------------------*/
    /*
     问题:
     1.std::pow( 1.0, 2 ),这个pow是指的数学里面的指数函数吗?
     
     理解:
     这个函数是IntentionNormalDribble的finished函数,主要作用是根据当前球员和球的状态,输出完成运球时相应的提示信息
     By ChenYanTing
     */
    bool
    IntentionNormalDribble::finished( const PlayerAgent * agent )
    {
        if ( M_turn_step + M_dash_step == 0 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (finished) check finished. empty queue" );
            return true;
        }
    
        const WorldModel & wm = agent->world();
    
        if ( M_last_execute_time.cycle() + 1 != wm.time().cycle() )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": finished(). last execute time is not match" );
            return true;
        }
    
        if ( wm.existKickableTeammate()
             || wm.existKickableOpponent() )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (finished). exist other kickable player" );
            return true;
        }
    
        if ( wm.ball().pos().dist2( M_target_point ) < std::pow( 1.0, 2 )
             && wm.self().pos().dist2( M_target_point ) < std::pow( 1.0, 2 ) )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (finished). reached target point" );
            return true;
        }
    
        const Vector2D ball_next = wm.ball().pos() + wm.ball().vel();
        if ( ball_next.absX() > ServerParam::i().pitchHalfLength() - 0.5
             || ball_next.absY() > ServerParam::i().pitchHalfWidth() - 0.5 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (finished) ball will be out of pitch. stop intention." );
            return true;
        }
    
        if ( wm.audioMemory().passRequestTime() == agent->world().time() )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (finished). heard pass request." );
            return true;
        }
    
        // playmode is checked in PlayerAgent::parse()
        // and intention queue is totally managed at there.
    
        dlog.addText( Logger::DRIBBLE,
                      __FILE__": (finished). not finished yet." );
    
        return false;
    }
    
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    /*
     问题:
     1.std::pow( 1.0, 2 ),这个pow是指的数学里面的指数函数吗?
     2.CooperativeAction的含义是什么?
     3.ActionChainGraph的含义是什么?
     
     理解:
     这个函数是IntentionNormalDribble的execute函数,主要作用是根据M_turn_step、M_dash_step等变量,来判断是否可以执行Dribble动作,并且返回相应的值
     By ChenYanTing
     */
    bool
    IntentionNormalDribble::execute( PlayerAgent * agent )
    {
        if ( M_turn_step + M_dash_step == 0 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute) empty queue." );
            return false;
        }
    
        dlog.addText( Logger::DRIBBLE,
                      __FILE__": (intention:execute) turn=%d dash=%d",
                      M_turn_step, M_dash_step );
    
        const WorldModel & wm = agent->world();
    
        //
        // compare the current queue with other chain action candidates
        //
    
        if ( wm.self().isKickable()
             && M_turn_step <= 0 )
        {
            CooperativeAction::Ptr current_action( new Dribble( wm.self().unum(),
                                                                M_target_point,
                                                                wm.ball().vel().r(),
                                                                0,
                                                                M_turn_step,
                                                                M_dash_step,
                                                                "queuedDribble" ) );
            current_action->setIndex( 0 );
            current_action->setFirstDashPower( ServerParam::i().maxDashPower() );
    
            ShortDribbleGenerator::instance().setQueuedAction( wm, current_action );
    
            ActionChainHolder::instance().update( wm );
            const ActionChainGraph & search_result = ActionChainHolder::i().graph();
            const CooperativeAction & first_action = search_result.getFirstAction();
    
            if ( first_action.category() != CooperativeAction::Dribble
                 || ! first_action.targetPoint().equals( current_action->targetPoint() ) )
            {
                agent->debugClient().addMessage( "CancelDribbleQ" );
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (intention:execute) cancel. select other action." );
                return false;
            }
        }
    
        //
        //
        //
    
        if ( checkOpponent( wm ) )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute). but exist opponent. cancel intention." );
            return false;
        }
    
        //
        // execute action
        //
    
        if ( M_turn_step > 0 )
        {
            if ( ! doTurn( agent ) )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (intention:exuecute) failed to turn. clear intention" );
                this->clear();
                return false;
            }
        }
        else if ( M_dash_step > 0 )
        {
            if ( ! doDash( agent ) )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (intention:execute) failed to dash.  clear intention" );
                this->clear();
                return false;
            }
        }
        else
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute). No command queue" );
            this->clear();
            return false;
        }
    
        dlog.addText( Logger::DRIBBLE,
                      __FILE__": (intention:execute). done" );
        agent->debugClient().addMessage( "NormalDribbleQ%d:%d", M_turn_step, M_dash_step );
        agent->debugClient().setTarget( M_target_point );
    
    
        if ( ! M_view_action )
        {
            if ( wm.gameMode().type() != GameMode::PlayOn
                 || M_turn_step + M_dash_step <= 1 )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (intention:execute) default view synch" );
                agent->debugClient().addMessage( "ViewSynch" );
                agent->setViewAction( new View_Synch() );
            }
            else
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (intention:execute) default view normal" );
                agent->debugClient().addMessage( "ViewNormal" );
                agent->setViewAction( new View_Normal() );
            }
        }
        else
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute) registered view" );
            agent->debugClient().addMessage( "ViewRegisterd" );
            agent->setViewAction( M_view_action->clone() );
        }
    
        if ( ! M_neck_action )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute) default turn_neck scan field" );
            agent->debugClient().addMessage( "NeckScan" );
            agent->setNeckAction( new Neck_TurnToBallOrScan( 0 ) );
        }
        else
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (intention:execute) registered turn_neck" );
            agent->debugClient().addMessage( "NeckRegistered" );
            agent->setNeckAction( M_neck_action->clone() );
        }
    
        M_last_execute_time = wm.time();
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    /*
     问题:
     1.nearest_opp->bodyCount(),这个bodyCount()是指的什么?
     2.CooperativeAction有必要弄清楚里面每个动作的作用吗?
     3.nearest_opp->vel().r(),r()是指的速度的横向还是纵向或者说是速度的绝对值?全称是什么。。。
     4.Line2D的对象opp_line指的敌人的防线吗?
    
     理解:
     这个函数是IntentionNormalDribble的checkOpponent函数,主要作用是根据球的下一个位置、敌人的状态来确认是否存在可能有威胁的敌人
     By ChenYanTing
     */
    bool
    IntentionNormalDribble::checkOpponent( const WorldModel & wm )
    {
        const Vector2D ball_next = wm.ball().pos() + wm.ball().vel();
    
        /*--------------------------------------------------------*/
        // exist near opponent goalie in NEXT cycle
        if ( ball_next.x > ServerParam::i().theirPenaltyAreaLineX()
             && ball_next.absY() < ServerParam::i().penaltyAreaHalfWidth() )
        {
            const PlayerObject * opp_goalie = wm.getOpponentGoalie();
            if ( opp_goalie
                 && opp_goalie->distFromBall() < ( ServerParam::i().catchableArea()
                                                   + ServerParam::i().defaultPlayerSpeedMax() )
                 )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": existOpponent(). but exist near opponent goalie" );
                this->clear();
                return true;
            }
        }
    
        const PlayerObject * nearest_opp = wm.getOpponentNearestToSelf( 5 );
    
        if ( ! nearest_opp )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": existOppnent(). No opponent" );
            return false;
        }
    
        /*--------------------------------------------------------*/
        // exist very close opponent at CURRENT cycle
        if (  nearest_opp->distFromBall() < ServerParam::i().defaultKickableArea() + 0.2 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": existOpponent(). but exist kickable opp" );
            this->clear();
            return true;
        }
    
        /*--------------------------------------------------------*/
        // exist near opponent at NEXT cycle
        if ( nearest_opp->pos().dist( ball_next )
             < ServerParam::i().defaultPlayerSpeedMax() + ServerParam::i().defaultKickableArea() + 0.3 )
        {
            const Vector2D opp_next = nearest_opp->pos() + nearest_opp->vel();
            // oppopnent angle is known
            if ( nearest_opp->bodyCount() == 0
                 || nearest_opp->vel().r() > 0.2 )
            {
                Line2D opp_line( opp_next,
                                 ( nearest_opp->bodyCount() == 0
                                   ? nearest_opp->body()
                                   : nearest_opp->vel().th() ) );
                if ( opp_line.dist( ball_next ) > 1.2 )
                {
                    // never reach
                    dlog.addText( Logger::DRIBBLE,
                                  __FILE__": existOpponent(). opp never reach." );
                }
                else if ( opp_next.dist( ball_next ) < 0.6 + 1.2 )
                {
                    dlog.addText( Logger::DRIBBLE,
                                  __FILE__": existOpponent(). but opp may reachable(1)." );
                    this->clear();
                    return true;
                }
    
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": existOpponent(). opp angle is known. opp may not be reached." );
            }
            // opponent angle is not known
            else
            {
                if ( opp_next.dist( ball_next ) < 1.2 + 1.2 ) //0.6 + 1.2 )
                {
                    dlog.addText( Logger::DRIBBLE,
                                  __FILE__": existOpponent(). but opp may reachable(2)." );
                    this->clear();
                    return true;
                }
            }
    
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": existOpponent(). exist near opp. but avoidable?" );
        }
    
        return false;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    /*
     问题:
     1.default_dist_thr这个是指的thresholds的意思吗?
     2.inertiaPoint的含义是什么?
     
     理解:
     这个函数是IntentionNormalDribble的doTurn函数,主要作用是根据目前身体角度、目标点的位置、惯性位置等因素,来判断是否需要执行转向
     By ChenYanTing
     */
    bool
    IntentionNormalDribble::doTurn( PlayerAgent * agent )
    {
        if ( M_turn_step <= 0 )
        {
            return false;
        }
    
        const double default_dist_thr = 0.5;
    
        const WorldModel & wm = agent->world();
    
        --M_turn_step;
    
        Vector2D my_inertia = wm.self().inertiaPoint( M_turn_step + M_dash_step );
        AngleDeg target_angle = ( M_target_point - my_inertia ).th();
        AngleDeg angle_diff = target_angle - wm.self().body();
    
        double target_dist = ( M_target_point - my_inertia ).r();
        double angle_margin
            = std::max( 15.0,
                        std::fabs( AngleDeg::atan2_deg( default_dist_thr,
                                                        target_dist ) ) );
    
        if ( angle_diff.abs() < angle_margin )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (doTurn)  but already facing. diff = %.1f  margin=%.1f",
                          angle_diff.degree(), angle_margin );
            this->clear();
            return false;
        }
    
        dlog.addText( Logger::DRIBBLE,
                      __FILE__": (doTurn) turn to (%.2f, %.2f) moment=%f",
                      M_target_point.x, M_target_point.y, angle_diff.degree() );
    
        agent->doTurn( angle_diff );
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    /*
     问题:
     1.accel_mag是什么意思?
     2.inertiaPoint的含义是什么?
     3.Vector2D中的rotatedVector,这个含义是什么?感觉像是极坐标?
     
     
     理解:
     这个函数是IntentionNormalDribble的doDash函数,主要作用是根据目前球与自身的一些状态进行综合判断,是否需要执行dash动作。
     By ChenYanTing
     */
    bool
    IntentionNormalDribble::doDash( PlayerAgent * agent )
    {
        if ( M_dash_step <= 0 )
        {
            return false;
        }
    
        const WorldModel & wm = agent->world();
        int self_min = wm.interceptTable()->selfReachCycle();
        int mate_min = wm.interceptTable()->teammateReachCycle();
        int opp_min = wm.interceptTable()->opponentReachCycle();
        const PlayerObject * fastest_opp = wm.interceptTable()->fastestOpponent();
        const PlayerPtrCont & opps = wm.opponentsFromSelf();
        const PlayerObject * nearest_opp
            = ( opps.empty()
                ? static_cast< PlayerObject * >( 0 )
                : opps.front() );
      const double nearest_opp_dist = ( nearest_opp
                                          ? nearest_opp->distFromSelf()
                                          : 1000.0 );
      const Vector2D nearest_opp_pos = ( nearest_opp
                                           ? nearest_opp->pos()
                                           : Vector2D( -1000.0, 0.0 ) );
        --M_dash_step;
    
        double dash_power
            = wm.self().getSafetyDashPower( ServerParam::i().maxDashPower() );
        if (fastest_opp && nearest_opp_dist < 3.0 && wm.self().pos().x >= nearest_opp_pos.x )
             dash_power = ServerParam::i().maxDashPower();
        double accel_mag = dash_power * wm.self().dashRate();
    
        Vector2D dash_accel = Vector2D::polar2vector( accel_mag, wm.self().body() );
    
        Vector2D my_next = wm.self().pos() + wm.self().vel() + dash_accel;
        Vector2D ball_next = wm.ball().pos() + wm.ball().vel();
        Vector2D ball_next_rel = ( ball_next - my_next ).rotatedVector( - wm.self().body() );
        double ball_next_dist = ball_next_rel.r();
    
        if ( ball_next_dist < ( wm.self().playerType().playerSize()
                                + ServerParam::i().ballSize() )
             )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (doDash) collision may occur. ball_dist = %f",
                          ball_next_dist );
            this->clear();
            return false;
        }
    
        if ( ball_next_rel.absY() > wm.self().playerType().kickableArea() - 0.1 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (doDash) next Y difference is over. y_diff = %f",
                          ball_next_rel.absY() );
            this->clear();
            return false;
        }
    
        // this dash is the last of queue
        // but at next cycle, ball is NOT kickable
        if ( M_dash_step <= 0 )
        {
            if ( ball_next_dist > wm.self().playerType().kickableArea() - 0.15 )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (doDash) last dash. but not kickable at next. ball_dist=%f",
                              ball_next_dist );
                this->clear();
                return false;
            }
        }
    
        if ( M_dash_step > 0 )
        {
            // remain dash step. but next dash cause over run.
            AngleDeg ball_next_angle = ( ball_next - my_next ).th();
            if ( ( wm.self().body() - ball_next_angle ).abs() > 90.0
                 && ball_next_dist > wm.self().playerType().kickableArea() - 0.2 )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (doDash) dash. but run over. ball_dist=%f",
                              ball_next_dist );
                this->clear();
                return false;
            }
        }
    
        dlog.addText( Logger::DRIBBLE,
                      __FILE__": (doDash) power=%.1f  accel_mag=%.2f",
                      dash_power, accel_mag );
        agent->doDash( dash_power );
    
        return true;
    }
    
    
    
    
    
    /*-------------------------------------------------------------------*/
    /*-------------------------------------------------------------------*/
    /*-------------------------------------------------------------------*/
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    Bhv_NormalDribble::Bhv_NormalDribble( const CooperativeAction & action,
                                          NeckAction::Ptr neck,
                                          ViewAction::Ptr view )
        : M_target_point( action.targetPoint() ),
          M_first_ball_speed( action.firstBallSpeed() ),
          M_first_turn_moment( action.firstTurnMoment() ),
          M_first_dash_power( action.firstDashPower() ),
          M_first_dash_angle( action.firstDashAngle() ),
          M_total_step( action.durationStep() ),
          M_kick_step( action.kickCount() ),
          M_turn_step( action.turnCount() ),
          M_dash_step( action.dashCount() ),
          M_neck_action( neck ),
          M_view_action( view )
    {
        if ( action.category() != CooperativeAction::Dribble )
        {
            M_target_point = Vector2D::INVALIDATED;
            M_total_step = 0;
        }
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
    */
    bool
    Bhv_NormalDribble::execute( PlayerAgent * agent )
    {
        const WorldModel & wm = agent->world();
    
        if ( ! wm.self().isKickable() )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). no kickable..." );
            return false;
        }
    
        if ( ! M_target_point.isValid()
             || M_total_step <= 0 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). illegal target point or illegal total step" );
    #ifdef DEBUG_PRINT
            std::cerr << wm.self().unum() << ' ' << wm.time()
                      << " Bhv_NormalDribble::execute() illegal target point or illegal total step"
                      << std::endl;
    #endif
            return false;
        }
    
        const ServerParam & SP = ServerParam::i();
    
        if ( M_kick_step > 0 )
        {
            Vector2D first_vel = M_target_point - wm.ball().pos();
            first_vel.setLength( M_first_ball_speed );
    
            const Vector2D kick_accel = first_vel - wm.ball().vel();
            const double kick_power = kick_accel.r() / wm.self().kickRate();
            const AngleDeg kick_angle = kick_accel.th() - wm.self().body();
    
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). first kick: power=%f angle=%f, n_turn=%d n_dash=%d",
                          kick_power, kick_angle.degree(),
                          M_turn_step, M_dash_step );
    
            if ( kick_power > SP.maxPower() + 1.0e-5 )
            {
                dlog.addText( Logger::DRIBBLE,
                              __FILE__": (execute) over the max power" );
    #ifdef DEBUG_PRINT
                std::cerr << __FILE__ << ':' << __LINE__ << ':'
                          << wm.self().unum() << ' ' << wm.time()
                          << " over the max power " << kick_power << std::endl;
    #endif
            }
    
            agent->doKick( kick_power, kick_angle );
    
            dlog.addCircle( Logger::DRIBBLE,
                            wm.ball().pos() + first_vel,
                            0.1,
                            "#0000ff" );
        }
        else if ( M_turn_step > 0 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). first turn: moment=%.2f, n_turn=%d n_dash=%d",
                          M_first_turn_moment,
                          M_turn_step, M_dash_step );
    
            agent->doTurn( M_first_turn_moment );
    
        }
        else if ( M_dash_step > 0 )
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). first dash: dash power=%.1f dir=%.1f",
                          M_first_dash_power, M_first_dash_angle.degree() );
            agent->doDash( M_first_dash_power, M_first_dash_angle );
        }
        else
        {
            dlog.addText( Logger::DRIBBLE,
                          __FILE__": (execute). no action steps" );
            std::cerr << __FILE__ << ':' << __LINE__ << ':'
                      << wm.self().unum() << ' ' << wm.time()
                      << " no action step." << std::endl;
            return false;
        }
    
    
       agent->setIntention( new IntentionNormalDribble( M_target_point,
                                                        M_turn_step,
                                                        M_total_step - M_turn_step - 1, // n_dash
                                                        wm.time() ) );
    
       if ( ! M_view_action )
       {
           if ( M_turn_step + M_dash_step >= 3 )
           {
               agent->setViewAction( new View_Normal() );
           }
       }
       else
       {
           agent->setViewAction( M_view_action->clone() );
       }
    
       if ( ! M_neck_action )
       {
           agent->setNeckAction( new Neck_ScanField() );
       }
       else
       {
           agent->setNeckAction( M_neck_action->clone() );
       }
    
        return true;
    }
    
    

    bhv_pass_kick_find_receiver.cpp

    代码个人理解与存在的问题(已经通过注释标出)

    注释的格式为:
    /*
    问题:
    1.xxxx
    2.xxxx

    理解:
    这个函数是xxxxx
    By ChenYanTing
    */

    
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
    
    #include "bhv_pass_kick_find_receiver.h"
    
    #include "action_chain_holder.h"
    #include "action_chain_graph.h"
    #include "field_analyzer.h"
    
    #include "neck_turn_to_receiver.h"
    
    #include <rcsc/action/bhv_scan_field.h>
    #include <rcsc/action/body_hold_ball.h>
    #include <rcsc/action/body_kick_one_step.h>
    #include <rcsc/action/body_smart_kick.h>
    #include <rcsc/action/body_stop_ball.h>
    #include <rcsc/action/body_turn_to_point.h>
    #include <rcsc/action/neck_scan_field.h>
    #include <rcsc/action/neck_turn_to_point.h>
    #include <rcsc/action/neck_turn_to_player_or_scan.h>
    #include <rcsc/action/view_synch.h>
    #include <rcsc/action/kick_table.h>
    #include <rcsc/player/player_agent.h>
    #include <rcsc/player/abstract_player_object.h>
    #include <rcsc/player/soccer_intention.h>
    #include <rcsc/player/intercept_table.h>
    #include <rcsc/player/say_message_builder.h>
    #include <rcsc/common/server_param.h>
    #include <rcsc/common/logger.h>
    #include <rcsc/math_util.h>
    
    #include <vector>
    #include <algorithm>
    #include <cmath>
    
    // #define DEBUG_PRINT
    
    using namespace rcsc;
    
    
    class IntentionPassKickFindReceiver
        : public SoccerIntention {
    private:
        int M_step;
        int M_receiver_unum;
        Vector2D M_receive_point;
    
    public:
    
        IntentionPassKickFindReceiver( const int receiver_unum,
                                       const Vector2D & receive_point )
            : M_step( 0 ),
              M_receiver_unum( receiver_unum ),
              M_receive_point( receive_point )
          { }
    
        bool finished( const PlayerAgent * agent );
    
        bool execute( PlayerAgent * agent );
    
    private:
    
    };
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
    无
     
     
     理解:
     这个函数是IntentionPassKickFindReceiver的finished函数,主要作用是根据kickable、reciever等因素在IntentionPassKickFindReceiver结束时返回一些提示
     By ChenYanTing
     */
    bool
    IntentionPassKickFindReceiver::finished( const PlayerAgent * agent )
    {
        ++M_step;
    
        dlog.addText( Logger::TEAM,
                      __FILE__": (finished) step=%d",
                      M_step );
    
        if ( M_step >= 2 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) time over" );
            return true;
        }
    
        const WorldModel & wm = agent->world();
    
        //
        // check kickable
        //
    
        if ( ! wm.self().isKickable() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) no kickable" );
            return true;
        }
    
        //
        // check receiver
        //
    
        const AbstractPlayerObject * receiver = wm.ourPlayer( M_receiver_unum );
    
        if ( ! receiver )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) NULL receiver" );
            return true;
        }
    
        if ( receiver->unum() == wm.self().unum() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) receiver is self" );
            return true;
        }
    
        if ( receiver->posCount() <= 0 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) already seen" );
            return true;
        }
    
        //
        // check opponent
        //
    
        if ( wm.existKickableOpponent() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) exist kickable opponent" );
            return true;
        }
    
        if ( wm.interceptTable()->opponentReachCycle() <= 1 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) opponent may be kickable" );
            return true;
        }
    
        //
        // check next kickable
        //
    
        double kickable2 = std::pow( wm.self().playerType().kickableArea()
                                     - wm.self().vel().r() * ServerParam::i().playerRand()
                                     - wm.ball().vel().r() * ServerParam::i().ballRand()
                                     - 0.15,
                                     2 );
        Vector2D self_next = wm.self().pos() + wm.self().vel();
        Vector2D ball_next = wm.ball().pos() + wm.ball().vel();
    
        if ( self_next.dist2( ball_next ) > kickable2 )
        {
            // unkickable if turn is performed.
            dlog.addText( Logger::TEAM,
                          __FILE__": (finished) unkickable at next cycle" );
            return true;
        }
    
        return false;
    }
    
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    Bhv_PassKickFindReceiver::Bhv_PassKickFindReceiver( const ActionChainGraph & chain_graph )
        : M_chain_graph( chain_graph )
    {
    }
    
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    */
    /*
     问题:
     1.AngleDeg类有什么用?
     2.M_receive_point是什么?为什么face_point = ( receiver_pos + M_receive_point ) * 0.5?无法理解这个计算公式
     
     
     
     理解:
     这个函数是IntentionPassKickFindReceiver的execute函数,主要作用是根据reciever的坐标还有自己身体的朝向来判断是否需要执行相应的转向接受者的动作
     By ChenYanTing
     */
    bool
    IntentionPassKickFindReceiver::execute( PlayerAgent * agent )
    {
        const WorldModel & wm = agent->world();
        const AbstractPlayerObject * receiver = wm.ourPlayer( M_receiver_unum );
    
        if ( ! receiver )
        {
            return false;
        }
    
        const Vector2D next_self_pos = agent->effector().queuedNextMyPos();
        const AngleDeg next_self_body = agent->effector().queuedNextMyBody();
        const double next_view_width = agent->effector().queuedNextViewWidth().width() * 0.5;
    
        const Vector2D receiver_pos = receiver->pos() + receiver->vel();
        const AngleDeg receiver_angle = ( receiver_pos - next_self_pos ).th();
    
        Vector2D face_point = ( receiver_pos + M_receive_point ) * 0.5;
        AngleDeg face_angle = ( face_point - next_self_pos ).th();
    
        double rate = 0.5;
        while ( ( face_angle - receiver_angle ).abs() > next_view_width - 10.0 )
        {
            rate += 0.1;
            face_point
                = receiver_pos * rate
                + M_receive_point * ( 1.0 - rate );
            face_angle = ( face_point - next_self_pos ).th();
    
            if ( rate > 0.999 )
            {
                break;
            }
        }
    
        dlog.addText( Logger::TEAM,
                      __FILE__": (intentin) facePoint=(%.1f %.1f) faceAngle=%.1f",
                      face_point.x, face_point.y,
                      face_angle.degree() );
        agent->debugClient().addMessage( "IntentionTurnToReceiver%.0f",
                                         face_angle.degree() );
        Body_TurnToPoint( face_point ).execute( agent );
        agent->setNeckAction( new Neck_TurnToPoint( face_point ) );
    
        return true;
    }
    
    
    
    /*-------------------------------------------------------------------*/
    /*-------------------------------------------------------------------*/
    /*-------------------------------------------------------------------*/
    
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     1.AngleDeg类有什么用?
     2.M_receive_point是什么?为什么face_point = ( receiver_pos + M_receive_point ) * 0.5?无法理解这个计算公式
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的execute函数,主要作用是根据isKickable、 receiver->unum()等变量来判断是否可以传球,如果不可以则返回false,否则传球并且返回true
     By ChenYanTing
     */
    bool
    Bhv_PassKickFindReceiver::execute( PlayerAgent * agent )
    {
        dlog.addText( Logger::TEAM,
                      __FILE__": Bhv_PassKickFindReceiver" );
    
        const WorldModel & wm = agent->world();
    
        if ( ! wm.self().isKickable() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": no kickable" );
            return false;
        }
    
        //
        // validate the pass
        //
        const CooperativeAction & pass = M_chain_graph.getFirstAction();
    
        if ( pass.category() != CooperativeAction::Pass )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": action(%d) is not a pass type.",
                          pass.category() );
            return false;
        }
    
        const AbstractPlayerObject * receiver = wm.ourPlayer( pass.targetPlayerUnum() );
    
        if ( ! receiver )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": NULL receiver." );
    
            return false;
        }
    
        dlog.addText( Logger::TEAM,
                      __FILE__": pass receiver unum=%d (%.1f %.1f)",
                      receiver->unum(),
                      receiver->pos().x, receiver->pos().y );
    
        if ( wm.self().unum() == receiver->unum() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": receiver is my self." );
            return false;
        }
    
        if ( receiver->unum() == 1 )
        {
            std::cerr << wm.time().cycle() << __FILE__ << ": DO NOT PASS TO OUR GOALIE !!!
    
    ";
            return false;
        }
    
        // if ( receiver->isGhost() )
        // {
        //     dlog.addText( Logger::TEAM,
        //                   __FILE__": receiver is a ghost." );
        //     return false;
        // }
    
        if ( wm.gameMode().type() == GameMode::PlayOn )
        {
            doPassKick( agent, pass );
            return true;
        }
    
    #if 1
        //ht: testing avoid own_goal
        if ( wm.gameMode().type() == 7 ) //GameMode::GoalKick
        {
            Body_KickOneStep( pass.targetPoint(),
                              pass.firstBallSpeed() ).execute( agent );
    
    
            agent->setNeckAction( new Neck_TurnToReceiver( M_chain_graph ) );
            doSayPass( agent, pass );
            //std::cerr << wm.time().cycle() << __FILE__ << wm.self().unum() << ": 应该没有乌龙球了吧!!!
    
    ";
    
            return true;
        }
    #endif //1
    
        //
        // estimate kick step
        //
        int kick_step = pass.kickCount(); //kickStep( wm, pass );
    
        if ( kick_step == 1
             //&& pass.targetPoint().x > -20.0
             && receiver->seenPosCount() <= 3 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": 1 step kick." );
            doPassKick( agent, pass );
            return true;
        }
    
        //
        // trying to turn body and/or neck to the pass receiver
        //
    
        if ( doCheckReceiver( agent, pass ) )
        {
            agent->debugClient().addCircle( wm.self().pos(), 3.0 );
            agent->debugClient().addCircle( wm.self().pos(), 5.0 );
            agent->debugClient().addCircle( wm.self().pos(), 10.0 );
    
            return true;
        }
    
        if ( ( kick_step == 1
               && receiver->seenPosCount() > 3 )
             || receiver->isGhost() )
        {
            agent->debugClient().addMessage( "Pass:FindHold2" );
            dlog.addText( Logger::TEAM,
                          __FILE__": (execute) hold ball." );
    
            Body_HoldBall( true,
                           pass.targetPoint(),
                           pass.targetPoint() ).execute( agent );
            doSayPass( agent, pass );
            agent->setNeckAction( new Neck_TurnToReceiver( M_chain_graph ) );
            return true;
        }
    
        //
        // pass kick
        //
    
        doPassKick( agent, pass );
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     1.pass.kickCount()的含义是什么?
     2.M_receive_point是什么?为什么face_point = ( receiver_pos + M_receive_point ) * 0.5?无法理解这个计算公式
     3.Body_KickOneStep和Body_SmartKick的作用是什么?在哪里可以找到?
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doPassKick函数,主要作用是执行身体转向并且执行传球动作
     By ChenYanTing
     */
    bool
    Bhv_PassKickFindReceiver::doPassKick( PlayerAgent * agent,
                                          const CooperativeAction & pass )
    {
        agent->debugClient().setTarget( pass.targetPlayerUnum() );
        agent->debugClient().setTarget( pass.targetPoint() );
        dlog.addText( Logger::TEAM,
                      __FILE__" (Bhv_PassKickFindReceiver) pass to "
                      "%d receive_pos=(%.1f %.1f) speed=%.3f",
                      pass.targetPlayerUnum(),
                      pass.targetPoint().x, pass.targetPoint().y,
                      pass.firstBallSpeed() );
    
        if ( pass.kickCount() == 1
             || agent->world().gameMode().type() != GameMode::PlayOn )
        {
            Body_KickOneStep( pass.targetPoint(),
                              pass.firstBallSpeed() ).execute( agent );
    
        }
        else
        {
            Body_SmartKick( pass.targetPoint(),
                            pass.firstBallSpeed(),
                            pass.firstBallSpeed() * 0.96,
                            3 ).execute( agent );
        }
        agent->setNeckAction( new Neck_TurnToReceiver( M_chain_graph ) );
        doSayPass( agent, pass );
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     1.这个函数的意义是什么?看不太懂
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doPassKick函数,主要作用是执行身体转向并且执行传球动作
     By ChenYanTing
     */
    int
    Bhv_PassKickFindReceiver::kickStep( const WorldModel & wm,
                                        const CooperativeAction & pass )
    {
        Vector2D max_vel
            = KickTable::calc_max_velocity( ( pass.targetPoint() - wm.ball().pos() ).th(),
                                            wm.self().kickRate(),
                                            wm.ball().vel() );
    
        // dlog.addText( Logger::TEAM,
        //               __FILE__": (kickStep) maxSpeed=%.3f passSpeed=%.3f",
        //               max_vel.r(), pass.firstBallSpeed() );
    
        if ( max_vel.r2() >= std::pow( pass.firstBallSpeed(), 2 ) )
        {
            return 1;
        }
    
        if ( pass.firstBallSpeed() > 2.5 ) // Magic Number
        {
            return 3;
        }
    
        return 2;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     无
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doPassKick函数,主要作用是根据目标点的坐标、自己的朝向、球的位置等等来判断是否有必要doCheckReceiver,如果没有返回false,如果有责返回true并且设置朝向面对reciever
     By ChenYanTing
     */
    bool
    Bhv_PassKickFindReceiver::doCheckReceiver( PlayerAgent * agent,
                                               const CooperativeAction & pass )
    {
        const WorldModel & wm = agent->world();
    
        double nearest_opp_dist = 65535.0;
        wm.getOpponentNearestTo( wm.ball().pos(), 10, &nearest_opp_dist );
        if ( nearest_opp_dist < 4.0 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (doCheckReceiver) exist near opponent. dist=%.2f",
                          nearest_opp_dist );
            return false;
        }
    
        const AbstractPlayerObject * receiver = wm.ourPlayer( pass.targetPlayerUnum() );
    
        if ( ! receiver )
        {
            return false;
        }
    
        if ( receiver->seenPosCount() == 0 )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (doCheckReceiver) receiver already seen." );
            return false;
        }
    
        //
        // set view mode
        //
        agent->setViewAction( new View_Synch() );
    
        const double next_view_half_width = agent->effector().queuedNextViewWidth().width() * 0.5;
        const double view_min = ServerParam::i().minNeckAngle() - next_view_half_width + 10.0;
        const double view_max = ServerParam::i().maxNeckAngle() + next_view_half_width - 10.0;
    
        //
        // check if turn_neck is necessary or not
        //
        const Vector2D next_self_pos = wm.self().pos() + wm.self().vel();
        const Vector2D player_pos = receiver->pos() + receiver->vel();
        const AngleDeg player_angle = ( player_pos - next_self_pos ).th();
    
        const double angle_diff = ( player_angle - wm.self().body() ).abs();
    
        if ( angle_diff < view_min
             || view_max < angle_diff )
        {
            //
            // need turn
            //
    
            dlog.addText( Logger::TEAM,
                          __FILE__": (doCheckReceiver) need turn."
                          " angleDiff=%.1f viewRange=[%.1f %.1f]",
                          angle_diff, view_min, view_max );
    
            //
            // TODO: check turn moment?
            //
    
    
            //
            // stop the ball
            //
    
            const double next_ball_dist = next_self_pos.dist( wm.ball().pos() + wm.ball().vel() );
            const double noised_kickable_area = wm.self().playerType().kickableArea()
                - wm.ball().vel().r() * ServerParam::i().ballRand()
                - wm.self().vel().r() * ServerParam::i().playerRand()
                - 0.15;
    
            dlog.addText( Logger::TEAM,
                          __FILE__": (doCheckReceiver) next_ball_dist=%.3f"
                          " noised_kickable=%.3f(kickable=%.3f)",
                          next_ball_dist,
                          noised_kickable_area,
                          wm.self().playerType().kickableArea() );
    
            if ( next_ball_dist > noised_kickable_area )
            {
                if ( doKeepBall( agent, pass ) )
                {
                    return true;
                }
    
                if ( Body_StopBall().execute( agent ) )
                {
                    dlog.addText( Logger::TEAM,
                                  __FILE__": (doCheckReceiver) stop the ball" );
                    agent->debugClient().addMessage( "PassKickFind:StopBall" );
                    agent->debugClient().setTarget( receiver->unum() );
    
                    return true;
                }
    
                dlog.addText( Logger::TEAM,
                              __FILE__": (doCheckReceiver) Could not stop the ball???" );
            }
    
            //
            // turn to receiver
            //
    
            doTurnBodyNeckToReceiver( agent, pass );
            return true;
        }
    
        //
        // can see the receiver without turn
        //
        dlog.addText( Logger::TEAM,
                      __FILE__": (doCheckReceiver) can see receiver[%d](%.2f %.2f)"
                      " angleDiff=%.1f viewRange=[%.1f %.1f]",
                      pass.targetPlayerUnum(),
                      player_pos.x, player_pos.y,
                      angle_diff, view_min, view_max );
    
        return false;
    
        // agent->debugClient().addMessage( "Pass:FindHold1" );
        // dlog.addText( Logger::TEAM,
        //               __FILE__": (doCheckReceiver) hold ball." );
    
        // Body_HoldBall( true,
        //                pass.targetPoint(),
        //                pass.targetPoint() ).execute( agent );
        // doSayPass( agent, pass );
        // agent->setNeckAction( new Neck_TurnToReceiver() );
    
        // return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     无
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doKeepBall函数,主要作用是根据getKeepBallVel()函数获得的保持球在keepArea区域的速度,与球自身的速度,来计算踢球的加速度和角度等,最后实现doKeepBall动作
     By ChenYanTing
     */
    bool
    Bhv_PassKickFindReceiver::doKeepBall( rcsc::PlayerAgent * agent,
                                          const CooperativeAction & pass )
    {
        Vector2D ball_vel = getKeepBallVel( agent->world() );
    
        if ( ! ball_vel.isValid() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (doKeepBall) no candidate." );
    
            return false;
        }
    
        //
        // perform first kick
        //
    
        Vector2D kick_accel = ball_vel - agent->world().ball().vel();
        double kick_power = kick_accel.r() / agent->world().self().kickRate();
        AngleDeg kick_angle = kick_accel.th() - agent->world().self().body();
    
        if ( kick_power > ServerParam::i().maxPower() )
        {
            dlog.addText( Logger::TEAM,
                          __FILE__": (doKeepBall) over kick power" );
            return false;
        }
    
        dlog.addText( Logger::TEAM,
                      __FILE__": (doKeepBall) "
                      " ballVel=(%.2f %.2f)"
                      " kickPower=%.1f kickAngle=%.1f",
                      ball_vel.x, ball_vel.y,
                      kick_power,
                      kick_angle.degree() );
    
        agent->debugClient().addMessage( "PassKickFind:KeepBall" );
        agent->debugClient().setTarget( pass.targetPlayerUnum() );
        agent->debugClient().setTarget( agent->world().ball().pos()
                                        + ball_vel
                                        + ball_vel * ServerParam::i().ballDecay() );
    
        agent->doKick( kick_power, kick_angle );
        agent->setNeckAction( new Neck_TurnToReceiver( M_chain_graph ) );
    
        //
        // set turn intention
        //
    
        dlog.addText( Logger::TEAM,
                      __FILE__": (doKeepBall) register intention" );
        agent->setIntention( new IntentionPassKickFindReceiver( pass.targetPlayerUnum(),
                                                                pass.targetPoint() ) );
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     无
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的getKeepBallVel函数,最重要的部分是for循环的内容,通过十二次遍历球员的一周,找到一个最佳位置的球的对应的速度,并且返回。
     By ChenYanTing
     */
    rcsc::Vector2D
    Bhv_PassKickFindReceiver::getKeepBallVel( const rcsc::WorldModel & wm )
    {
        static GameTime s_update_time( 0, 0 );
        static Vector2D s_best_ball_vel( 0.0, 0.0 );
    
        if ( s_update_time == wm.time() )
        {
            return s_best_ball_vel;
        }
        s_update_time = wm.time();
    
        //
        //
        //
    
        const int ANGLE_DIVS = 12;
    
        const ServerParam & SP = ServerParam::i();
        const PlayerType & ptype = wm.self().playerType();
        const double collide_dist2 = std::pow( ptype.playerSize()
                                               + SP.ballSize(),
                                               2 );
        const double keep_dist = ptype.playerSize()
            + ptype.kickableMargin() * 0.5
            + ServerParam::i().ballSize();
    
        const Vector2D next_self_pos
            = wm.self().pos() + wm.self().vel();
        const Vector2D next2_self_pos
            = next_self_pos
            + wm.self().vel() * ptype.playerDecay();
    
        //
        // create keep target point
        //
    
        int best_angle = -1;
        Vector2D best_ball_vel = Vector2D::INVALIDATED;
        int best_opponent_step = 0;
        double best_ball_speed = 1000.0;
    
    
        for ( int a = 0; a < ANGLE_DIVS; ++a )
        {
            Vector2D keep_pos
                = next2_self_pos
                + Vector2D::from_polar( keep_dist,
                                        360.0/ANGLE_DIVS * a );
            if ( keep_pos.absX() > SP.pitchHalfLength() - 0.2
                 || keep_pos.absY() > SP.pitchHalfWidth() - 0.2 )
            {
                continue;
            }
    
            Vector2D ball_move = keep_pos - wm.ball().pos();
            double ball_speed = ball_move.r() / ( 1.0 + SP.ballDecay() );
    
            Vector2D max_vel
                = KickTable::calc_max_velocity( ball_move.th(),
                                                wm.self().kickRate(),
                                                wm.ball().vel() );
            if ( max_vel.r2() < std::pow( ball_speed, 2 ) )
            {
                continue;
            }
    
            Vector2D ball_next_next = keep_pos;
    
            Vector2D ball_vel = ball_move.setLengthVector( ball_speed );
            Vector2D ball_next = wm.ball().pos() + ball_vel;
    
            if ( next_self_pos.dist2( ball_next ) < collide_dist2 )
            {
                ball_next_next = ball_next;
                ball_next_next += ball_vel * ( SP.ballDecay() * -0.1 );
            }
    
    #ifdef DEBUG_PRINT
            dlog.addText( Logger::TEAM,
                          "(getKeepBallVel) %d: ball_move th=%.1f speed=%.2f max=%.2f",
                          a,
                          ball_move.th().degree(),
                          ball_speed,
                          max_vel.r() );
            dlog.addText( Logger::TEAM,
                          "__ ball_next=(%.2f %.2f) ball_next2=(%.2f %.2f)",
                          ball_next.x, ball_next.y,
                          ball_next_next.x, ball_next_next.y );
    #endif
    
            //
            // check opponent
            //
    
            int min_step = 1000;
            for ( PlayerPtrCont::const_iterator o = wm.opponentsFromSelf().begin();
                  o != wm.opponentsFromSelf().end();
                  ++o )
            {
                if ( (*o)->distFromSelf() > 10.0 )
                {
                    break;
                }
    
                int o_step = FieldAnalyzer::predict_player_reach_cycle( *o,
                                                                        ball_next_next,
                                                                        (*o)->playerTypePtr()->kickableArea(),
                                                                        0.0, // penalty distance
                                                                        1, // body count thr
                                                                        1, // default turn step
                                                                        0, // wait cycle
                                                                        true );
    
                if ( o_step < 0 )                                          //change
                {
                    break;
                }
    
                if ( o_step < min_step )
                {
                    min_step = o_step;
                }
            }
    #ifdef DEBUG_PRINT
            dlog.addText( Logger::TEAM,
                          __FILE__": (getKeepBallVel) %d: keepPos=(%.2f %.2f)"
                          " ballNext2=(%.2f %.2f) ballVel=(%.2f %.2f) speed=%.2f o_step=%d",
                          a,
                          keep_pos.x, keep_pos.y,
                          ball_next_next.x, ball_next_next.y,
                          ball_vel.x, ball_vel.y,
                          ball_speed,
                          min_step );
    #endif
            if ( min_step > best_opponent_step )
            {
                best_angle = a;
                best_ball_vel = ball_vel;
                best_opponent_step = min_step;
                best_ball_speed = ball_speed;
            }
            else if ( min_step == best_opponent_step )
            {
                if ( best_ball_speed > ball_speed )
                {
                    best_angle = a;
                    best_ball_vel = ball_vel;
                    best_opponent_step = min_step;
                    best_ball_speed = ball_speed;
                }
            }
        }
    
        s_best_ball_vel = best_ball_vel;
        return s_best_ball_vel;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     无
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doTurnBodyNeckToReceiver函数,主要通过往循环遍历,找到一个最合适的身体朝向,然后将身体转向reciever
     By ChenYanTing
     */
    bool
    Bhv_PassKickFindReceiver::doTurnBodyNeckToReceiver( PlayerAgent * agent,
                                                        const CooperativeAction & pass )
    {
        const ServerParam & SP = ServerParam::i();
        const WorldModel & wm = agent->world();
    
        const AbstractPlayerObject * receiver = wm.ourPlayer( pass.targetPlayerUnum() );
    
        const double next_view_half_width = agent->effector().queuedNextViewWidth().width() * 0.5;
        const double view_min = SP.minNeckAngle() - next_view_half_width + 10.0;
        const double view_max = SP.maxNeckAngle() + next_view_half_width - 10.0;
    
        //
        // create candidate body target points
        //
    
        std::vector< Vector2D > body_points;
        body_points.reserve( 16 );
    
        const Vector2D next_self_pos = wm.self().pos() + wm.self().vel();
        const Vector2D receiver_pos = receiver->pos() + receiver->vel();
        const AngleDeg receiver_angle = ( receiver_pos - next_self_pos ).th();
    
        const double max_x = SP.pitchHalfLength() - 7.0;
        const double min_x = ( max_x - 10.0 < next_self_pos.x
                               ? max_x - 10.0
                               : next_self_pos.x );
        const double max_y = std::max( SP.pitchHalfLength() - 5.0,
                                       receiver_pos.absY() );
        const double y_step = std::max( 3.0, max_y / 5.0 );
        const double y_sign = sign( receiver_pos.y );
    
        // on the static x line (x = max_x)
        for ( double y = 0.0; y < max_y + 0.001; y += y_step )
        {
            body_points.push_back( Vector2D( max_x, y * y_sign ) );
        }
    
        // on the static y line (y == receiver_pos.y)
        for ( double x_rate = 0.9; x_rate >= 0.0; x_rate -= 0.1 )
        {
            double x = std::min( max_x,
                                 max_x * x_rate + min_x * ( 1.0 - x_rate ) );
            body_points.push_back( Vector2D( x, receiver_pos.y ) );
        }
    
        //
        // evaluate candidate points
        //
    
        const double max_turn
            = wm.self().playerType().effectiveTurn( SP.maxMoment(),
                                                    wm.self().vel().r() );
        Vector2D best_point = Vector2D::INVALIDATED;
        double min_turn = 360.0;
    
        const std::vector< Vector2D >::const_iterator p_end = body_points.end();
        for ( std::vector< Vector2D >::const_iterator p = body_points.begin();
              p != p_end;
              ++p )
        {
            AngleDeg target_body_angle = ( *p - next_self_pos ).th();
            double turn_moment_abs = ( target_body_angle - wm.self().body() ).abs();
    
    #ifdef DEBUG_PRINT
            dlog.addText( Logger::TEAM,
                          "____ body_point=(%.1f %.1f) angle=%.1f moment=%.1f",
                          p->x, p->y,
                          target_body_angle.degree(),
                          turn_moment_abs );
    #endif
    
            double angle_diff = ( receiver_angle - target_body_angle ).abs();
            if ( view_min < angle_diff
                 && angle_diff < view_max )
            {
                if ( turn_moment_abs < max_turn )
                {
                    best_point = *p;
    #ifdef DEBUG_PRINT
                    dlog.addText( Logger::TEAM,
                                  "____ oooo can turn and look" );
    #endif
                    break;
                }
    
                if ( turn_moment_abs < min_turn )
                {
                    best_point = *p;
                    min_turn = turn_moment_abs;
    #ifdef DEBUG_PRINT
                    dlog.addText( Logger::TEAM,
                                  "____ ---- update candidate point min_turn=%.1f",
                                  min_turn );
    #endif
                }
            }
    #ifdef DEBUG_PRINT
            else
            {
                dlog.addText( Logger::TEAM,
                              "____ xxxx cannot look" );
            }
    #endif
        }
    
        if ( ! best_point.isValid() )
        {
            best_point = pass.targetPoint();
    #ifdef DEBUG_PRINT
            dlog.addText( Logger::TEAM,
                          __FILE__": (doTurnBodyNeckToPoint) could not find the target point." );
    #endif
        }
    
        //
        // perform the action
        //
        agent->debugClient().addMessage( "PassKickFind:Turn" );
        agent->debugClient().setTarget( receiver->unum() );
    
        dlog.addText( Logger::TEAM,
                      __FILE__": (doTurnBodyNeckToReceiver)"
                      " receiver=%d receivePos=(%.2f %.2f)"
                      " turnTo=(%.2f %.2f)",
                      pass.targetPlayerUnum(),
                      pass.targetPoint().x, pass.targetPoint().y,
                      best_point.x, best_point.y );
        agent->debugClient().addLine( next_self_pos, best_point );
    
        Body_TurnToPoint( best_point ).execute( agent );
        doSayPass( agent, pass );
        agent->setNeckAction( new Neck_TurnToReceiver( M_chain_graph ) );
    
        return true;
    }
    
    /*-------------------------------------------------------------------*/
    /*!
    
     */
    /*
     问题:
     无
     
     理解:
     这个函数是Bhv_PassKickFindReceiver的doSayPass函数,作用是告知reciever我要传球了
     By ChenYanTing
     */
    void
    Bhv_PassKickFindReceiver::doSayPass( PlayerAgent * agent,
                                         const CooperativeAction & pass )
    {
        const int receiver_unum = pass.targetPlayerUnum();
        const Vector2D & receive_pos = pass.targetPoint();
    
        if ( agent->config().useCommunication()
             && receiver_unum != Unum_Unknown
             && ! agent->effector().queuedNextBallKickable()
             )
        {
            const AbstractPlayerObject * receiver = agent->world().ourPlayer( receiver_unum );
            if ( ! receiver )
            {
                return;
            }
    
            dlog.addText( Logger::ACTION | Logger::TEAM,
                          __FILE__": (doSayPass) set pass communication." );
    
            Vector2D target_buf( 0.0, 0.0 );
    
            agent->debugClient().addMessage( "Say:pass" );
    
            Vector2D ball_vel( 0.0, 0.0 );
            if ( ! agent->effector().queuedNextBallKickable() )
            {
                ball_vel = agent->effector().queuedNextBallVel();
            }
    
            agent->addSayMessage( new PassMessage( receiver_unum,
                                                   receive_pos + target_buf,
                                                   agent->effector().queuedNextBallPos(),
                                                   ball_vel ) );
        }
    }
    
    

    学习心得以及体会

    这一次学习过程,有两个明显的感受。第一个是自己看代码的速度越来越快了,对于深度的要求也拿捏的比以前更加准确了,这是值得高兴的。第二个是感觉自己即使看完了所有的代码,心里没有底,因为我看代码的过程是漫无目的的看的。像是一种了解式的读代码,没有带着任何的问题或者关键去看。所以不知道有没有办法,让我们可以进行一种目的性的学习,比如,布置一些让我们修改某个区域的代码实现某个功能的小任务。我认为可能这样的学习效率和积极度都会更高。但是也有可能我们目前的水平还不足以支撑我们实现上述的任务。那这样的话,我会继续加油的!

  • 相关阅读:
    websocket 工作原理
    Flask中的wtforms使用
    DBUtils
    Django模板语言与视图(view)
    Django之图书管理系统
    Django的安装创建与连接数据库
    pymyspl模块
    多表查询与索引
    表的关系与查询
    mysql的数据类型与表约束
  • 原文地址:https://www.cnblogs.com/782687539-nanfu/p/12707602.html
Copyright © 2020-2023  润新知