/** 以最快的方式跑到目标点 * Run to the destination point with the fastest method. * \param agent the agent itself. * \param act an atomic action caculated to go to pos for this cycle. * \param pos the destination point. * \param buffer point buffer, means the real destinantion is a cycle with the certer * of pos and radius of buffer. * \param power the intend power to use. * \param can_inverse true means can run in the inverse direction of the agent's body. * \param turn_first true means that the agent should turn first to adjust the direction. */ void Dasher::GoToPoint(Agent & agent, AtomicAction & act, Vector target, double buffer, double power, bool can_inverse, bool turn_first) { act.Clear(); const Vector & my_pre_pos = agent.Self().GetPredictedPos(1); double dist = my_pre_pos.Dist(target); if (dist < buffer){ act.mSucceed = true; return; } AngleDeg dirdiff = GetNormalizeAngleDeg((target - my_pre_pos).Dir() - agent.GetSelf().GetBodyDir()); if (fabs(dirdiff) < 90.0){ can_inverse = false; //不需要倒着跑 } bool inverse = false; if (can_inverse){ int cycle = CycleNeedToPointWithCertainPosture(agent.Self(), target, true); if (cycle < 9 && cycle < CycleNeedToPointWithCertainPosture(agent.Self(), target, false)){ inverse = true; } } GoToPointWithCertainPosture(agent, act, target, buffer, power, inverse, turn_first); }
/** * 将身体转向特定方向 * Turn body to a certain direction. * @param agent the agent itself. * @param ang the angle to turn to. * @return an atomic action to turn the body. */ AtomicAction Dasher::GetTurnBodyToAngleAction(const Agent & agent, AngleDeg ang) { AtomicAction action; action.mType = CT_Turn; double angle_turn = GetNormalizeAngleDeg(ang - agent.GetSelf().GetBodyDir()); if (fabs(angle_turn) < agent.GetSelf().GetMaxTurnAngle()) { action.mSucceed = true; action.mTurnAngle = angle_turn; } else { action.mSucceed = false; action.mTurnAngle = Sign(angle_turn) * agent.GetSelf().GetMaxTurnAngle(); } return action; }
/** * Update data used by tackle. * \param agent. */ void Tackler::UpdateTackleData(const Agent & agent) { if (mAgentID == agent.GetAgentID()) { return; /** no need to update */ } mAgentID = agent.GetAgentID(); const BallState & ball_state = agent.GetWorldState().GetBall(); const PlayerState & player_state = agent.GetSelf(); Vector ball_2_player = (ball_state.GetPos() - player_state.GetPos()).Rotate(-player_state.GetBodyDir()); Vector ball_vel; mMaxTackleSpeed = -1.0; mDirMap.clear(); const double max_tackle_power = ServerParam::instance().maxTacklePower(); const double min_back_tackle_power = ServerParam::instance().maxBackTacklePower(); double factor = 1.0 - 0.5 * (fabs(Deg2Rad(ball_2_player.Dir())) / M_PI); for (AngleDeg tackle_angle = -180.0 + FLOAT_EPS; tackle_angle <= 180.0 + FLOAT_EPS; tackle_angle += 1.0) { Vector ball_vel(ball_state.GetVel()); double eff_power = (min_back_tackle_power + ((max_tackle_power - min_back_tackle_power) * (1.0 - fabs(Deg2Rad(tackle_angle)) / M_PI))) * ServerParam::instance().tacklePowerRate(); eff_power *= factor; ball_vel += Polar2Vector(eff_power, tackle_angle + player_state.GetBodyDir()); ball_vel = ball_vel.SetLength(Min(ball_vel.Mod(), ServerParam::instance().ballSpeedMax())); int angle_idx = ang2idx(tackle_angle); int dir_idx = dir2idx(ball_vel.Dir()); mTackleAngle[angle_idx] = tackle_angle; mBallVelAfterTackle[angle_idx] = ball_vel; mDirMap[dir_idx].push_back(std::make_pair(angle_idx, ang2idx(tackle_angle + 1.0))); if (ball_vel.Mod() > mMaxTackleSpeed){ mMaxTackleSpeed = ball_vel.Mod(); } if (ball_vel.Mod() * ServerParam::instance().ballDecay() < FLOAT_EPS) { mCanTackleStopBall = true; mTackleStopBallAngle = tackle_angle; } } // // // for (AngleDeg tackle_angle = -180.0 + 0.3; tackle_angle <= 180.0 + FLOAT_EPS; tackle_angle += 1.0) { // Vector ball_vel = GetBallVelAfterTackle(agent, tackle_angle); // AngleDeg tackle_angle2 = 0.0; // GetTackleAngleToDir(agent, ball_vel.Dir(), & tackle_angle2); // // std::cout << tackle_angle << " " << tackle_angle2 << std::endl; // } // // exit(0); }
/** 以确定的姿势(指倒着跑和正跑),跑到目标点 * Run to the destination point with a certain posture, forward or backword. * \param agent the agent itself. * \param act an atomic action caculated to go to pos for this cycle. * \param pos the destination point. * \param buffer point buffer, means the real destinantion is a cycle with the center * of pos and radius of buffer. * \param power the intend power to use. * \param inverse true means run backward. * \param turn_first true means that the agent should turn first to adjust the direction. */ void Dasher::GoToPointWithCertainPosture(Agent & agent, AtomicAction & act, Vector target, double buffer, double power, const bool inverse, bool turn_first) { act.Clear(); const Vector & my_pre_pos = agent.Self().GetPredictedPos(1); double dist = my_pre_pos.Dist(target); AngleDeg dirdiff = inverse? GetNormalizeAngleDeg((my_pre_pos - target).Dir() - agent.GetSelf().GetBodyDir()): GetNormalizeAngleDeg((target - my_pre_pos).Dir() - agent.GetSelf().GetBodyDir()); //需要转身的角度 double bufang; buffer = Max(0.05, buffer); //TODO: wait test if (buffer > FLOAT_EPS){ bufang = ASin(buffer / dist / 2) * 2; bufang = Max(10.0, bufang); } else { bufang = 0.0; } if (fabs(dirdiff) > bufang){ TurnDashPlaning(agent, act, target, buffer, power, inverse, turn_first); } else { power = inverse? -fabs(power): fabs(power); power = agent.GetSelf().CorrectDashPowerForStamina(power); if (fabs(power) > FLOAT_EPS){ act.mType = CT_Dash; act.mDashPower = AdjustPowerForDash(agent.Self(), target, buffer, power); act.mDashPower = agent.GetSelf().CorrectDashPowerForStamina(act.mDashPower); if (fabs(act.mDashPower) > FLOAT_EPS){ act.mSucceed = true; } else { act.mType = CT_None; //power为零时就不要执行了,没意义 TurnDashPlaning(agent, act, target, buffer, power, inverse, turn_first);//再次考虑转身,以免漏掉一线好的转身的机会 } } } }
/** * Caculate the get ball cycles and actions. * @param agent the agent itself. * @param act the atomic action to execute this cycle to get the ball. * @param int_cycle the beginning cycle to do the action, while -1 means now. * @param can_inverse true means allow runnning backwards to get the ball. * @param turn_first true means the agent turn first to get the ball. * @return a double to show the cycles needed and if it less than 0, that is to say impossible. */ double Dasher::GetBall(Agent & agent, AtomicAction & act, int int_cycle , bool can_inverse, bool turn_first) { Assert(int_cycle == -1 || int_cycle >= 0); if (int_cycle == -1) { double min_diff = HUGE_VALUE; const int max_consider_cycle = Min(int(MobileState::Predictor::MAX_STEP), agent.GetStrategy().GetSureOppInterCycle() - 1); for (int i = 0; i <= max_consider_cycle; ++i) { Vector target = agent.GetWorldState().GetBall().GetPredictedPos(i); double my_cycle = RealCycleNeedToPoint(agent.GetSelf(), target, can_inverse); double diff = fabs(my_cycle - i + 1.0); if (diff < min_diff) { min_diff = diff; int_cycle = i; } } } if (int_cycle == -1) { int_cycle = Max(0, agent.GetStrategy().GetMyInterCycle()); } //int_cycle = 1; //std::cout << "Max consider cycle " << int_cycle << std::endl; //std::cout << "Predicted Ball position " << agent.GetWorldState().GetBall().GetPredictedPos(int_cycle).X() << " " << agent.GetWorldState().GetBall().GetPredictedPos(int_cycle).Y() << std::endl; //std::cout << "Ball position " << agent.GetWorldState().GetBall().GetPos().X() << " " << agent.GetWorldState().GetBall().GetPos().Y() << std::endl; GoToPoint( agent, act, agent.GetWorldState().GetBall().GetPredictedPos(int_cycle), agent.GetSelf().GetKickableArea() - GETBALL_BUFFER, agent.GetSelf().CorrectDashPowerForStamina(ServerParam::instance().maxDashPower()), can_inverse, turn_first ); return RealCycleNeedToPoint(agent.GetSelf(), agent.GetWorldState().GetBall().GetPredictedPos(int_cycle), can_inverse); }
/*! This method determines the optimal dash power to mantain an optimal speed When the current speed is too high and the distance is very small, a negative dash is performed. Otherwise the difference with the maximal speed is determined and the dash power rate is set to compensate for this difference. \param posRelTo relative point to which we want to dash \param angBody body angle of the agent \param vel current velocity of the agent \param dEffort current effort of the player \param iCycles desired number of cycles to reach this point \return dash power that should be sent with dash command */ bool Dasher::GetPowerForForwardDash(const Agent &agent, double* dash_power, Vector posRelTo, double angBody, double dEffort, int iCycles ) { // the distance desired is the x-direction to the relative position we // we want to move to. If point lies far away, we dash maximal. Furthermore // we subtract the x contribution of the velocity because it is not necessary // to dash maximal. if (!dash_power) { return false; } double dDist = posRelTo.Rotate(-angBody).X(); // get distance in direction if( iCycles <= 0 ) iCycles = 1; double dAcc = dDist * (1 - agent.GetSelf().GetPlayerDecay()) / (1 - pow(agent.GetSelf().GetPlayerDecay(), iCycles));//get the first Geom // get speed to travel now if( dAcc > agent.GetSelf().GetEffectiveSpeedMax() ) // if too far away { dAcc = agent.GetSelf().GetEffectiveSpeedMax(); // set maximum speed } //dAcc -= vel.rotate(-angBody).x; // subtract current velocity // acceleration = dash_power * dash_power_rate * effort -> // dash_power = acceleration / (dash_power_rate * effort ) double dDashPower = dAcc / (agent.GetSelf().GetDashPowerRate() * dEffort ); if( dDashPower > ServerParam::instance().maxDashPower()){ *dash_power = ServerParam::instance().maxDashPower(); return false; } else if( dDashPower < ServerParam::instance().minDashPower() ){ *dash_power = ServerParam::instance().minDashPower(); return false; } else{ *dash_power = dDashPower; return true; } }
/** * 考虑转身或直接dash * Planing to turn or to dash. * @param agent the agent itself. * @param act an atomic action to return the caculated decision. * @param target the target position to go to. * @param buffer * @param power the intend power for dash. * @param inverse true means running backwards. * @param turn_first true means turn first manually. */ void Dasher::TurnDashPlaning(Agent & agent, AtomicAction & act, Vector target, double buffer, double power, bool inverse, bool turn_first) { act.Clear(); power = inverse? -fabs(power): fabs(power); power = agent.GetSelf().CorrectDashPowerForStamina(power); PlayerState & self = agent.Self(); const Vector & my_pre_pos = self.GetPredictedPos(1); double target_dist = my_pre_pos.Dist(target); AngleDeg target_ang = inverse? GetNormalizeAngleDeg((my_pre_pos - target).Dir() - agent.GetSelf().GetBodyDir()): GetNormalizeAngleDeg((target - my_pre_pos).Dir() - agent.GetSelf().GetBodyDir()); //需要转身的角度 //DT_none //处理转身问题,可能1dash + 1turn更好 double bufang; if(buffer > FLOAT_EPS){ bufang = ASin(buffer / target_dist / 2) * 2; bufang = Max(10.0, bufang); } else{ bufang = 0.0; } const double & effort = self.GetEffort(); const double & accrate = self.GetDashPowerRate(); const double & inertia_moment = self.GetInertiaMoment(); const double & decay = self.GetPlayerDecay(); const double & speedmax = self.GetEffectiveSpeedMax(); const Vector & vel = self.GetVel(); const double & facing = self.GetBodyDir(); double speed = vel.Mod(); double diffang = fabs(target_ang); double oneturnang = self.GetMaxTurnAngle(); if(buffer < FLOAT_EPS && target_dist > 2.6){ oneturnang += 2.6; } if(turn_first || diffang <= oneturnang || speed < 0.04){ //由于噪声的存在,在非身体方向也有速度,调也调不过来,太小时不如直接转,0.04是一般队员的误差极值 act.mType = CT_Turn; act.mTurnAngle = target_ang; act.mSucceed = true; return; } else { //计算1 dash + 1turn; //下面都是算dash到什么速度最好 double mspd1 = (180.0/ ( diffang + 10 ) - 1.0 ) / inertia_moment; //10是buf,不然容易调整后正好还转不过来 double dash2spd = Min(mspd1 / decay, speedmax) / (1 + ServerParam::instance().playerRand()); if( fabs(GetNormalizeAngleDeg(vel.Dir() - facing)) > 90.0){ speed = -speed; } double spd_inf = Max(speed - accrate * ServerParam::instance().maxDashPower(), -dash2spd); double spd_sup = Min(speed + accrate * ServerParam::instance().maxDashPower(), dash2spd); Ray cur_r(self.GetPos(), facing); Vector pt_inf = cur_r.GetPoint(spd_inf * (1.0 + decay)); Vector pt_sup = cur_r.GetPoint(spd_sup * (1.0 + decay)); Vector pt;//要取得点 bool bnegtive;//要跑的点是不是在身后 Line pdl = Line(cur_r).GetPerpendicular(target);//垂线 if(pdl.IsPointInSameSide(pt_inf, pt_sup)){ if(fabs(GetNormalizeAngleDeg(facing-target_ang)) < 90.0){ pt = pt_sup; bnegtive = (spd_sup<0); } else{ pt = pt_inf; bnegtive = (spd_inf<0); } } else{ double dist; bnegtive = !cur_r.Intersection(pdl, dist); //垂点 pt = cur_r.GetPoint(dist); } double dis = pt.Dist(target);//两个周期后离point的距离,用来compare with 2 turn double twoturnang = oneturnang + 180.0 / (1.0 + inertia_moment * fabs(speed) * decay); if(twoturnang > diffang){ const Vector & pt2 = self.GetPredictedPos(2); double dis2 = pt2.Dist(target); if(dis2 < dis || diffang > 102.6){ //一次转太大,误差也大不如直接2turn act.mType = CT_Turn; act.mTurnAngle = target_ang; act.mSucceed = true; return; } } double spd_need; if(bnegtive){ spd_need = -(pt - self.GetPos()).Mod() / (1.0 + decay); } else{ spd_need = (pt - self.GetPos()).Mod() / (1.0 + decay); } //会总是dash而dash后又转不过来...,看着就像不拿球 double turnang_after_dash = 180.0 / (1.0 + inertia_moment * fabs(spd_need) * decay) - 5.0; if(GetAngleDegDiffer((target - pt).Dir(), facing) > turnang_after_dash ){ act.mType = CT_Turn; act.mTurnAngle = target_ang; act.mSucceed = true; return; } double power = ( spd_need - speed ) / accrate / effort; power = MinMax(-ServerParam::instance().maxDashPower(), power, ServerParam::instance().maxDashPower()); //I over act.mType = CT_Dash; act.mDashPower = AdjustPowerForDash(self, pt, FLOAT_EPS, power); act.mDashPower = agent.GetSelf().CorrectDashPowerForStamina(act.mDashPower); if(fabs(act.mDashPower) > FLOAT_EPS) { act.mSucceed = true; } else { act.mType = CT_Turn; //power为零时就不要执行了,没意义 act.mTurnAngle = 0.0; act.mSucceed = true; } } }
ActionEffector::ActionEffector(Agent & agent): mAgent( agent ), mWorldState( agent.GetWorldState() ), mBallState( agent.GetWorldState().GetBall()), mSelfState( agent.GetSelf()), mTurn( agent ), mDash( agent ), mTurnNeck( agent ), mSay( agent ), mAttentionto( agent ), mKick( agent ), mTackle( agent ), mPointto( agent ), mCatch( agent ), mMove( agent ), mChangeView( agent ), mCompression( agent ), mSenseBody( agent ), mScore( agent ), mBye( agent ), mDone( agent ), mClang( agent ), mEar( agent ), mSynchSee( agent ), mChangePlayerType( agent ), mStart( agent ), mChangePlayMode( agent ), mMovePlayer( agent ), mMoveBall( agent ), mLook( agent ), mTeamNames( agent ), mRecover( agent ), mCheckBall( agent ) { mTurnCount = 0; mDashCount = 0; mTurnNeckCount = 0; mSayCount = 0; mAttentiontoCount = 0; mKickCount = 0; mTackleCount = 0; mPointtoCount = 0; mCatchCount = 0; mMoveCount = 0; mChangeViewCount = 0; mCompressionCount = 0; mSenseBodyCount = 0; mScoreCount = 0; mByeCount = 0; mDoneCount = 0; mClangCount = 0; mEarCount = 0; mSynchSeeCount = 0; mChangePlayerTypeCount = 0; mIsMutex = false; mIsTurn = false; mIsDash = false; mIsTurnNeck = false; mIsSay = false; mIsAttentionto = false; mIsKick = false; mIsTackle = false; mIsPointto = false; mIsCatch = false; mIsMove = false; mIsChangeView = false; mIsCompression = false; mIsSenseBody = false; mIsScore = false; mIsBye = false; mIsDone = false; mIsClang = false; mIsEar = false; mIsSynchSee = false; mIsChangePlayerType = false; mIsSayMissed = false; mLastCommandType = CT_None; }