ロボカップサッカーは、人間のサッカーの試合と同じく、自分で考えて動く自律移動型ロボットを使った競技会形式で行われる。
競技会は研究成果の実験・発表の場であるとともに、一般の方も先端科学と技術に接することができる交流の場でもある。
小型ロボットリーグ
直径18cm高さ15cmの小型ロボット5台が1チームとなり、6.5mx4.5mのフィールドで試合をするリーグ。 フィールド全体を見渡すカメラ、あるいはロボット搭載カメラからの視覚情報をベースに、ロボット同士がいかにシステマチックなチームプレーを構築していくかが見所である。
中型ロボットリーグ
縦横50cm未満の中型ロボットが6台でチームを構成し18m×12m大のフィールドで試合をするリーグ。試合時間は15分ハーフ。 多くのチームが360度見渡せるカメラを搭載し、自分とボールの位置をすばやく判断して動く。
4足ロボットリーグ
四足のエンターテイメントロボットが4台1チームでサッカーをするリーグ。フィールドの大きさは4m×6m。ロボットの性能が同じであるため、各チームのプログラミングの優劣が勝敗を左右する。
ヒューマノイドリーグ
2002年の世界大会から正式種目となった自律型2足歩行ロボットによるリーグ。 3対3の複数ロボットによる試合形式の競技が行われている。
マイクロロボットリーグ
シチズン・大阪大学共同で開発した超小型ロボット「Eco-Be!」を利用するリーグ。 全ての競技の中で最も機体が小さいのが特徴である。
シミュレーションリーグ
実機を使うことなく,コンピュータ上の仮想フィールドで異なった人工知能プログラミングされた11対11のプレーヤーがサッカーを行うロボカップ最古参リーグ。 ドリブル等の個人技や、ワンツーパスといったチーム戦略の両面において洗練されたプレーを見ることができる。高さのない2次元フィールドでの競技と、ヒューマノイド型ロボットが技を競う3次元フィールドでの競技がある。
シミュレーションリーグの基本的なルールとしては、フィールドの大きさは縦68m×横105mのフィールド上にチーム11名の選手が参加できる。各選手とボールは、0.1秒を1ステップとする時刻ごとにフィールド上を移動する。選手は毎ステップごとにコマンドを実行することができ、コマンドの実行結果はフィールド上に5分ハーフの計10分間で行われるので各選手ごとに約6000手のコマンドを実行することで、チームの勝利を狙うことになる。
シミュレーションリーグは、サーバ・クライアント方式を採用しており、作業を一つのプログラムで処理するのではなく、作業を依頼するクライアントと作業を処理するサーバという二種類のプロセスで分担して処理する。 サッカーシミュレータはサーバとクライアントがUDP/IPで通信するモデルとなっている。
図1のようにロボカップサッカシミュレータはサーバ・クライアントシステムで設計されている。試合を実行するには11体のプレイヤエージェントなどの動作を制御するクライアントプログラムを2チーム分用意し、サーバへ接続する。仮想的なフィールドはサッカーサーバとサッカーモニタとが通信することで視覚化される。 サーバとクライアントプログラムとの通信はUDP/IPによって行われる。通信プロトコルはLISPのS式という形式の文字列を摸したものとなっている。
サッカーの試合での試合状態はサッカーサーバではプレイモードという試合状態を表す文字列で示すようになっている。以下に試合開始から試合終了までのプレイモードの流れを示す。
各プレイモードの詳細
図2のようなプレイモードという文字列で表された状態によって、試合を進行する。 まず、サッカーサーバーが起動した時点ではプレイモードはbefore_kick_offになる。両チームの選手がフィールドに存在し、サッカーモニター上でキックオフを行うまではプレイモードはbefore_kick_offのままになる。キックオフを開始した場合には、プレイモードがkick_off_lまたはkick_off_rとなる。
次に、最初にボールを蹴る側のプレイヤーがキックオフ後にボールを蹴った時、プレイモードはplay_onになる。ここでプレイモードがplay_on中の試合状況によって図2のように、コーナーキック、ゴールキック、キックイン、フリーキックなどを行う。行った後に、また試合状況はplay_onに戻り、ハーフタイムまたは試合終了になるまでこの流れを繰り返していく。
自陣がフィールドのどちら側にあるのか(左か右か)、選手の背番号、試合の現在の状態(キックオフ前など)などの情報が送られてくる。
この情報は、前半・後半の初めと、プレイヤーを起動させた直後にサーバから送られてくる。
その視覚情報の時間、オブジェクトの名前(ボール、旗、選手)、そのオブジェクトとの相対距離、オブジェクトの相対的な方向、オブジェクトの相対的な位置の変化、オブジェクトの相対的な方向の変化、オブジェクト(選手)の体の相対方向、オブジェクト(選手)の頭の相対方向が情報として送られてくる。
例のsee 0は、time=0の視覚情報を受信している。(goal r)61.6 36は、右サイドのゴール(goal_r)が61.6m離れたところに36度の位置に見えることを示している。
その感覚情報の時間、視野の広さ、スタミネ、速さ、頭の方向、kickカウント、dashカウント、turnカウント、sayカウント、turn_neckカウントが情報として送られてくる。
その聴覚情報の時間、メッセージの送り手の方向、メッセージ内容が情報として送られてくる。
1ステップごとの選手の速度の減衰率、ダッシュ係数、スタミナ最大値、スタミナの減少境界、スタミナ減少値、リカバリー最小値、ダッシュ実行率減少の境界、ダッシュ実行率増加の境界、ダッシュ実行率の減少値、ダッシュ実行率の増加値が情報として送られてくる。
現在は未実装。
ダッシュ係数、 スタミナ回復最大値、 予備スタミナ、 ダッシュ実行率最大値、 ダッシュ実行率最小値が情報として送られてくる。
選手がフィールドに登録されていない時などにエラーメッセージが送られてくる。
選手の向いている方向をMomentに従って変化させる。Momentの範囲は-180〜180度でプラスは時計回り、マイナスは反時計回りに回る。
Angleを選手の首の角度に加える。首の角度は体の角度に対して-90〜90の間でならない。体を回転させるturnコマンドが実行されると首のむいてる方向も変化する。
プレイヤーが向いている方向に向かって、Powerの値だけ速度を増加させる。実際の加速度はこの値に0.006倍をした値になる。Powerの範囲は-100〜100の間になっている。もし、パワーがマイナスの数なら、プレイヤーは後ろ向きにダッシュする。
ボールが十分近くにあるなら、Directionの方向にPowerの分だけボールを蹴る。実際の加速度の大きさは0.027倍の数値になる。Powerの範囲は0~100、Directionの範囲は-180〜180となっていまる。
座標(X,Y)にプレイヤーを瞬間移動させる。原点はセンターマークであり、X軸は相手のゴールに向かって伸び、Y軸はX軸に対して右側のタッチラインに向かって伸びている。このコマンドは、キックオフ前のときと、キーパーがボールをキャッチした直後にのみ使用できる。
Direction方向にあるボールをキャッチしようとする。このコマンドはキーパーだけに許されており、Directionの範囲は-180〜180。キーパーは幅1と長さ2で、方向がDirectionの長方形の中にボールがあるときにキャッチできる。
Messageを他のプレイヤーに伝えるために声を出す。Messageは英数字で10文字以下の文字列であり、受け取ることのできる最大距離がある。
視野の視野角度と視覚情報の品質を変えるコマンド。ANGLE_WIDTHはwide(180度) Normal(90度)narrow(45度)のうちいずれか一つを選ぶ。QUALITYはhighかlowのどちらかを選ぶ。highクオリティの場合、サーバから物体の距離・方向の情報を受け取れるが、lowクオリティの場合、物体の距離情報が落ち、方向の情報しか選手は受け取れない。初期値は各選手ともnormalとhighになっている。
そのほかにもタックル、腕を動かす動作、チーム監督・コーチの実装や選手の交代などがある。
本研究では、「JavaでつくるRoboCupサッカー選手プログラム」のプログラムを基に研究に取り組んだ。この本は全部で30章まであり、第5章から第30章にかけてプログラムの説明をしている。 第1章ではロボカップサッカーシミュレーションリーグだけでなく、その他のロボカップサッカーの種目について紹介してある。 第2章ではロボカップサッカーシミュレーション部門での試合ルールなどが説明されている。 第3章では選手の基本的な動作のルールについて説明されている。 第4章ではサッカークライアントを開発、シミュレーションを行うために事前に行っておく作業などが説明されている。
このプログラムはPlayerLv00クラスからPlayerLv27まで27個のクラスを00から順に継承しているプログラムである。 以下に簡潔にそれぞれのクラスの機能を書いていく。
改良前プログラムではサーバーからのメッセージを試合が終わるまで受信し続けるrunメソッドを主としてそれぞれのサッカー選手の行動を決めていく。
永久ループを作成し、スレッドの動作を終了させないようにする。このrunメソッドによって繰り返し他のメソッドを実行することによってプレイヤーの行動の決定や様々な予測を行っている。
このrunメソッド内にあるanalyzeMessageメソッドによってサーバーから送られてくるメッセージを解析し、それぞれの行動に分岐させる役割を持っている。
以上のような “init”、“see”、“sense_body”、“hear”、“server_param”、“player_param”、“player_type”がサーバーからメッセージとして送られてきた時に行動を分岐する。
メッセージからm_strSide(守るゴール)、m_iNumber(背番号)、m_strPlayMode(プレイモード)を切り取って取得。moveコマンドが実行される前に3m間隔で画面上部に並んだとき(選手登録前の状態)の座標を計算する。
視覚情報が到着した時刻をm_iVisualTimeに保存する。getLandMarkerメソッドで大文字を解決し、首の方向と体の方向を計算し、自分の位置を計算する。次に視覚情報にボール情報があるか検査し、ボール情報があればパラメータを読み込み、相対座標を求める。自分の絶対座標と相対座標からボールの絶対座標とする。速度を求めるためにDistChangeとDirChangeをgetParamメソッドで受け取り、自分を中心とした極座標系へ変換する。フィールドの座標系上での相対速度に変換し、自分の速度を足してボールの絶対速度を求める。視覚情報到着時にボールを見失ったか検査する。ボールが見えている時は探索カウント(m_iSearchCount)を常に0にする。checkFreshメソッドでボールが見えていないか検査し、ボールが見えていない時は探索カウントを検査し、0まで減っているときはカウントを9とする。自分の絶対座標が正確に計算されているかチェックする(されていない場合はこのメソッドを動作しない)。視界にある選手情報をgetObjectListメソッドで抜き出し、変換してそれぞれリストに入れていく。
時刻が最近の時刻であるか検査する。最近ではない場合はfalse、最近の場合はtrue。
キーワードで始まる物体データをメッセージから順に取り出す。キーワード情報(strObject)を抜き出し、getObjectMessage(strObject)メソッドに渡して得た様々な情報を配列に入れていく。
物体データのフィールド座標系での情報を計算する。対象のプレイヤーが敵か味方かでチーム文字列を分ける(味方・・・friend、敵・・・enemy)。通常の選手ならば背番号を抜き出し、ゴールキーパーならばgoalieと抜き出す。読み込んだ文字列表記のパラメータを数値に変換する。物体データ(プレイヤーデータ)のフィールドの位置を出した後、速度の計算を行う(極座標系→相対速度→絶対速度)。物体データ(プレイヤーデータ)の体と首の角度を計算する。全ての計算結果を文字列であらわす。
例・・・ | (team friend) | (number 6) | (x -10) | (y 5) | (vx 1) | (vy -2) | (body 30) | (neck 0) |
味方 | 6番 | X | Y | 速度X | 速度Y | 体 | 首 |
sense_bodyの後ろにある情報をm_iTimeに入れる。ハーフタイムごとにスタミナ系の計算パラメータを全回復させる。getParam(speed)メソッドでパラメータの取得と速度の大きさの計算をし、X方向、Y方向の速度成分の計算をして地面に対するスピードの計算を行う。getParam(head_angle)メソッドで体を基準とした方向メッセージを解析し、体の方向の計算を行う。
メッセージからstrSpeaker(発言者)、strContent(内容)を切り取って取得。もしレフェリーが笛を鳴らした場合、プレイモードの内容を解析。味方プレイヤーの声を抜き取り解析する。トラップ成功の聴覚メッセージanalyzeTrapMessageメソッドとパス地点を示す聴覚メッセージanalyzePassMessageメソッドで解析する。
トラップしていない時は何も返さない。視界にいる全ての選手の中でトラップ中の味方にパスが可能かどうかcheckPassableメソッドで検査する。trueの場合sayPassMessageメソッドでパスのための座標を発信する。
トラップ中の味方から自分へのパス価値の情報を集め、パスを出す選手位置の価値を計算する。パスの価値が現在のボールの位置の価値より大きいならばtrue、小さいならfalseを返す。
パスする目標座標をsendメソッドでサーバーに送る。
“p”から始まる場合・・・パス地点を配列に格納する。
メッセージをm_strServerParamに代入する。サーバーパラメータ内でこのクラスの計算に必要なもの{player_decay(減衰率),dash_power_rate(ダッシュ係数)}を得る。stamina_max(スタミナ最大値),recover_dec_thr(スタミナの減少境界),recover_dec(スタミナ減少値),recover_min(リカバリー最小値),effort_dec_thr(ダッシュ実行率減少の境界),effort_inc_thr(ダッシュ実行率増加の境界),effort_dec(ダッシュ実行率の減少値),effort_inc(ダッシュ実行率の増加値)の取り出し。getParam(maxneckang)メソッドで首が時計方向へ回る最大値の取り出し、getParam(minneckang)メソッドで首が反時計方向へ回る最小値の取り出しを行う。getParam(ball_decay)メソッドでボールの1ステップあたりの速度減衰率、getParam(player_decay)メソッドでプレイヤー速度のステップごとの減衰率、getParam(ball_size)メソッドでボールの大きさ、getParam(kick_power_rate)メソッドでキックパワーが加速度に変更されるときの係数を取得する。
現在は空であり、開発途中である。
id(背番号)の後ろにある情報をそれぞれのプレイヤータイプ配列に入れていく。dash_power_rate(ダッシュ係数),stamina_inc_max(スタミナ回復最大値),extra_stamina(予備スタミナ),effort_max(ダッシュ実行率最大値),effort_min(ダッシュ実行率最小値)の取り出しを行う。getParam(inertia_moment)メソッドで回転モーメントの取り出しを行う。getParam(player_size)メソッドでプレイヤーの大きさ、getParam(kickable_margin)メソッドでキック可能距離の取り出しを行う。getParam(player_speed_max)メソッドでplayer_speed_maxの取り出しを行う。
今回のドリブルの実装には“sense_body”で始まるメッセージがサーバから送られてきたときのplayメソッドで実行する。まず、playを行う前に予測を行うpredictメソッドについて以下に説明する。
predictMoveCommandメソッド,predictDashCommandメソッド,predictTurnCommandメソッド,predictKickCommandメソッドを実行する。それぞれMoveコマンド、Dashコマンド、Turnコマンド、Kickコマンドの解析を行う その解析結果によって予測を作る。
その後、playメソッドで様々な状況での選手の行動を行うようにしている。playメソッドについては以下に説明する。
checkInitialModeメソッドで初期フォーメーションに移動するか検査し、キックオフ前やゴール直後に移動するようにする。通常の動作では、ボールが視野に入っているかcheckFreshメソッドで検査し、入っている場合はplayWithBallメソッドを行う。入っていない場合は、searchBallメソッドを行う。
カウント(m_iSearchCount)が9の場合に首を右に、6の場合に首を左に、3の場合に後ろを向く行動をとる。
この本プログラムの選手の試合を見た評価としては、選手がボールを持った際に相手選手が来るまでどのような状況でもキープをし続ける。ボールをキープしている選手の特定の範囲内に相手が入ってきたらパスを行うという行動をしている。この相手選手が来るまでどのような状況でもキープし続ける行動を変えることで、もう少しいい試合運びができると感じた。
2章で述べた通り改良前プログラムでは主にボールを持っている選手はパス価値に添って最適だと判断された選手に向かってパスを行う。そして、パスを受けた選手は相手選手が決められた範囲内に近づくまでキープし続けるといったものである。主にパスが主体となっており、どんな状況でもパスかシュートしか行わず相手ゴールに向かう戦略としては不十分であった。例えば、図3では、黄色の9番の選手がボールを持っている。9番にとってはパス相手がいなく、相手選手との間に空間がある。この時、相手選手が近づいてくるまでボールをキープし近づいてきたらパスをするといった行動をする。
ここで、このような状況になった時、パスをするまでそこにキープし続けるのではなく、ドリブルを行い相手を抜いたりパスをするタイミングをずらすことで改良する前のプログラムより相手ゴールに近づく可能性が増える。図4と図5のように、9番の選手が相手選手との間にある空間に向かってドリブルを行い、味方の8番の選手のパス価値をあげることによって相手ゴールへと近づくことができたらベストである。
このような行動を起こす回数を増やせることによってシュートをする回数が増え、より相手のプログラムに勝つ確率が上がると考えた。
まず、ドリブルを実装するためにどういった試合状況でドリブルするかを考えた。ここで、元のプログラムにある選手がボールを持った時、他の味方選手に対してのパスの価値を計算するメソッドがある。このパスの価値を計算するメソッドの内容が以下の通りである。
ボールの移動先の座標と相手ゴールの座標の距離を計算し、この距離が大きいほど価値を低くする。パスの途中のコースに相手選手がいたら価値を0にする。
主にこのgetPassValueメソッドで決められる選手の価値の範囲は0~100であり、100に一番近い選手が一番パスをするのに最適であることを意味する。元のプログラムではパス価値の計算結果からそれぞれの行動を行う。パス価値が0~100の中で100に近い選手を1人だけ選びその選手にパスを行い、0の場合(価値を計算した選手全員が0)、相手ゴールに向かって蹴る動作を行う。
今回のプログラムの改良で、このパス価値を利用し、パス価値が低い場合の時にドリブルを行うということを実装した。
getDistanceメソッドでボールとの距離を計り、キック可能距離よりもボールとの距離が大きかった場合はfalseを、小さかった場合はtrueを返す。
x0,y0とx1,y1との距離を計算する。
まず、このメソッドではmaxgetValueメソッドを呼び出し、最大となる価値を求める。そして、その求めた最大価値を使いキック可能な時とキック不可能な時の行動を分岐した。キック可能な時(checkKickableがtrueの時)にパス価値が一定の値(本プログラムでは20と設定)以下の時にドリブルを行うということを実装した。また、ドリブルをある目標地点まで行うことにするとシュートを打てる場面でもシュートを打たないので、相手ゴールエリア内にボールを持っている選手が入ったらシュートを行うためにシュートメソッドも実装した。シュート・ドリブル行動を行わないとき(2つの行動の条件に当てはまらない時)はパス価値に沿ってパスを行うようにした。
ボールを持っている選手の視界に見える自分から5~45mの範囲内の全味方選手の中でパス価値をgetPassValueメソッドで求めていく。その求めた味方選手のそれぞれの価値を比べて一番大きい値になった選手のパス価値を求める。
今回実装したdribleメソッドはまず、パス価値が低いときのドリブルする位置を決める。今回はそのドリブルする位置を相手ゴールライン上のX座標と相手ゴール付近の2つのコーナーフラッグの間をY座標として設定した。そして、その2つの座標とボールを持っている選手との距離を計算し、距離が短いほう(座標が近いほう)に向かってドリブルをするように実装した。
このメソッドではそれぞれの守るゴールに対しての相手ゴール位置を設定する。その設定した相手ゴール位置に向かって一番強いシュート(kick 100)を行う。
本プログラムで試合を行ったところ次のような局面が現れた。図から見てわかるが、ボールを持っている6番の選手以外で今相手ゴールに近い味方選手(以下黄色とする)は7番、8番、10番である。ここで図6から、黄色7番はボールを持っている選手に近すぎており、相手の10番(以下青色とする)も黄色7番に近いためパス価値がないと判断できる。また、黄色8番の選手も間に青色10番がいるためパス価値がないと判断でき、黄色10番も青色8番が近くにいるためパス価値がないと判断できる。
この場面で実装したドリブルが機能するので図7と図8のように、黄色6番は青色9番の選手が近づくまでドリブルを行う。ここで、図8を見てわかるが図6の時と違って黄色6番の選手がドリブルをしたことによって黄色8番の選手がパスをする選手の候補としてあがったことがわかる。
結果的に図9、図10のように黄色6番の選手は味方選手の中でパス価値を計算し、一番高いパス価値になった黄色8番の選手にパスをした。黄色6番がドリブルを行い、ドリブルを行う前より違った状況にすることで新たにパスをする候補を見つけることができた。そして、8番の選手がフリーでボールを持つことができ、より相手ゴールに近づいていくチャンスが生まれた。
次に、改良したプログラムと改良前のプログラムとの試合結果を表で記す。結果は、左が改良前プログラムの得点であり右が改良前プログラムの得点である。ドリブルは、その試合でドリブルを行うことにより相手選手を抜いたり、相手ゴールに近づけた時の回数である。今回は20試合行った。
試合 | 結果 | ドリブル | 試合 | 結果 | ドリブル |
---|---|---|---|---|---|
1 | 2-2 | 8回 | 11 | 0-2 | 5回 |
2 | 1-0 | 6回 | 12 | 1-1 | 6回 |
3 | 2-3 | 4回 | 13 | 2-3 | 7回 |
4 | 1-0 | 6回 | 14 | 2-2 | 5回 |
5 | 2-0 | 5回 | 15 | 1-2 | 5回 |
6 | 1-0 | 6回 | 16 | 2-1 | 10回 |
7 | 1-0 | 7回 | 17 | 1-1 | 8回 |
8 | 2-2 | 9回 | 18 | 2-1 | 8回 |
9 | 2-2 | 7回 | 19 | 1-2 | 4回 |
10 | 3-1 | 8回 | 20 | 1-0 | 6回 |
結果としては、改良したプログラムは9勝5敗6引き分けとなった。表1から勝った試合や引き分けの試合の時に、ドリブルが有効になった回数が多いことがわかる。また、勝ち負けだけを判断すると改良前のプログラムより良くなっていることがわかる。
改良したプログラムでは改良する前のプログラムより前に向かうプレーが多くなり得点を決めるチャンスも多くなった。実際に行った試合結果で、得点を決めた所では、ドリブルからシュートをして得点を決めたり、ドリブルシュートから繋がった得点などもあった。また、シュートの判断をする範囲を改良する前のプログラムより広くしたことも得点に繋がっていると思う。
しかし、今の改良したプログラムでは、パス価値が0の時に相手ゴール付近のある点に向かってドリブルをするので、ドリブルする道上に相手がいる場合すぐにボールをとられてしまうことが多い。表1の試合結果の中で負けた試合では、無理なドリブルを行い相手選手にボールを取られてしまい、得点を決められてしまう場面が多かった。また、基本的には改良前のプログラムと同じなので、ドリブル以外では行動が同じであり、パスの具合によってもドリブルができるかできないかが分かれていた。
ドリブルをする際に、相手の位置をドリブルする選手が把握して一番最適だと思われるドリブルをすることや、相手の次に移動する位置を予測しながら相手にカットされない位置までドリブルをしてその後のプレイに繋ぐことによってもっと良いドリブルを行うことができると思った。
playWithBallメソッド
01: protected void playWithBall() { 02: int t = m_iTime; 03: double a = 0.0; 04: a =maxgetValue(a); 05: if (checkKickable()) { 06: if(m_strSide.startsWith("r") && m_dBallX[t] < -30.0 && (m_dBallY[t] > -20.0 && m_dBallY[t] < 20.0) || m_strSide.startsWith("l") && m_dBallX[t] > 30.0 && (m_dBallY[t] > -20.0 && m_dBallY[t] < 20.0)){ 07: shoot(); 08: }else if(a < 20.0 && m_strPlayMode.startsWith("play_on") && m_iNumber != 1 && m_iNumber != 2 && m_iNumber != 3 && m_iNumber != 4 && m_iNumber != 5){ 09: drible(); 10: }else{ 11: setKickTarget(); 12: kick(); 13: } 14: 15: } 16: else { 17: setMoveTarget(); 18: move(); 19: } 20: predict(t, t + 1); 21: setFaceTarget(); 22: lookAt(m_dFaceX[t], m_dFaceY[t]); 23:}
maxgetValueメソッド
01:protected double maxgetValue(double a) { 02: int t = m_iTime; 03: int i; 04: for (i = 0; i < m_listPlayer.size(); i++) { 05: String player1 = m_listPlayer.get(i).toString(); 06: double friendDir = OUT_OF_RANGE; 07: double friendDist = OUT_OF_RANGE; 08: double max_score = 0.0; 09: if (player1.indexOf("friend") > -1) { 10: double friendX = getParam(player1, "x", 1); 11: double friendY = getParam(player1, "y", 1); 12: friendDir = getDirection(m_dX[t], m_dY[t], friendX, friendY); 13: friendDist = getDistance(m_dX[t], m_dY[t], friendX, friendY); 14: if (friendDist < 5.0 || friendDist > 45.0) 15: friendDist = friendDir = OUT_OF_RANGE; 16: double score = getPassValue(m_dX[t], m_dY[t], friendX, friendY); 17: if (score > max_score) { 18: max_score = score; 19: a = max_score; 20: } 21: } 22: } 23: return a; 24:}
dribleメソッド
01:protected void drible(){ 02: int j; 03: int t = m_iTime; 04: double goalX,goalY,driX = 0.0,driY = 0.0; 05: if(m_strSide.startsWith("r")){ 06: double dist = getDistance(m_dBallX[t], m_dBallY[t], -52.5, 20.0); 07: double dist1 = getDistance(m_dBallX[t], m_dBallY[t], -52.5, -20.0); 08: if(dist > dist1){ 09: driX = -52.5;driY = -20.0; 10: }else{ 11: driX = -52.5;driY = 20.0; 12: } 13: goalX = -52.5;goalY = 0.0; 14: }else{ 15: double dist = getDistance(m_dBallX[t], m_dBallY[t], 52.5, 20.0); 16: double dist1 = getDistance(m_dBallX[t], m_dBallY[t], 52.5, -20.0); 17: if(dist > dist1){ 18: driX = 52.5;driY = -20.0; 19: }else{ 20: driX = 52.5;driY = 20.0; 21: } 22: goalX = 52.5;goalY = 0.0; 23: } 24: lookAt(goalX,goalY); 25: DecimalFormat f = new DecimalFormat("###0.00"); 26: double kickAX = driX - m_dBallX[t] - m_dBallVX[t]; 27: double kickAY = driY - m_dBallY[t] - m_dBallVY[t]; 28: double rad = Math.atan2(kickAY, kickAX); 29: double kick_dir = normalizeAngle(Math.toDegrees(rad) - m_dBody[t]); 30: m_strCommand[t] = 31: "(kick 10 "+ f.format(kick_dir) + ")"; 32:}
shootメソッド
01:protected void shoot(){ 02: int t = m_iTime; 03: double goalX,goalY; 04: if(m_strSide.startsWith("r")){ 05: goalX = -52.5;goalY = 5.0; 06: }else{ 07: goalX = 52.5;goalY = 5.0; 08: } 09: if(m_strPlayMode.startsWith("play_on")){ 10: DecimalFormat f = new DecimalFormat("###0.00"); 11: double kickAX = goalX - m_dBallX[t] - m_dBallVX[t]; 12: double kickAY = goalY - m_dBallY[t] - m_dBallVY[t]; 13: double rad = Math.atan2(kickAY, kickAX); 14: double kick_dir = normalizeAngle(Math.toDegrees(rad) - m_dBody[t]); 15: m_strCommand[t] = 16: "(kick 100 "+ f.format(kick_dir) + ")"; 17: } 18:}