import java.io.*; import java.util.*; import java.net.*; import java.awt.geom.*; import java.util.regex.*; /** * NUIS ローカルロボカップ サンプルプログラム * created by Toyohisa Nakada (2008.11.15) */ public class SamplePlayer0 implements Runnable{ /** 視覚情報に対応する処理を行う */ protected void onSeeMessage(SExp exp) throws Exception{ if(m_opposing_team_name.equals("unknown")){ m_opposing_team_name = getOpposingTeamName(exp); } if(checkInitialMode()){ // プレイ中以外の場合 // 各背番号ごとに決められた所定の位置に移動する。 Point2D.Double pos = getKickOffPosition(m_number); send("(move "+pos.x+" "+pos.y+")"); }else{ // プレイ中の場合 // 自分の向きを取得する。 double my_direction = getDirection(exp); // 自分の位置を取得する。 Point2D.Double my_position = getPosition(exp,my_direction); // ボールの情報を取得する Obj ball = getObject(exp,Pattern.compile("\\([b|B]\\)"),my_position,my_direction); // 味方のゴール情報を取得する Obj our_goal = getObject(exp,"(g "+m_side+")"); // 相手のゴール情報を取得する。 Obj opposing_goal = getObject(exp,"(g "+(m_side.equals("r")?"l":"r")+")"); // 味方のプレイヤを取得する。 Vector our_players = getObjects(exp,Pattern.compile("\\(p \""+m_our_team_name+"\".*"),my_position,my_direction); // 相手のプレイヤを取得する。 Vector opposing_players = getObjects(exp,Pattern.compile("\\(p \""+m_opposing_team_name+"\".*"),my_position,my_direction); if(ball != null){ // ボールが見えている場合 if(Math.abs(ball.getDirection()) < 20.0){ // ボールの見える角度が20.0度未満の場合 if(ball.getDistance() < 1.0){ // ボールまでの距離が1.0未満の場合 // ゴールに向かってキックする。 send(kick(exp,opposing_goal,my_direction)); }else{ // ボールまでの距離が1.0以上の場合 if(isNearest(exp,ball,our_players)){ // 自分がボールに一番近いと思っている場合 // ダッシュする。(ボールの方向をすでに向いているので、そのままダッシュすればボールに近づく。) send("(dash 80)"); }else{ // 自分以外のプレイヤがボールに近いと判断した場合 if(our_goal != null && our_goal.getDistance() > 50.0){ // 味方のゴールから離れすぎている場合 // 少し戻る。(味方のゴールが見えているので、そのままダッシュすれば味方のゴール方向に返ることになる) send("(dash 5)"); } } } }else{ // ボールの見える角度が20.0度以上の場合 // ゴール方向へ体の向きを変える。 send("(turn "+ball.getDirection()+")"); } }else{ // ボールが見えていない場合 // 20度だけ回転してボールを捜す。 send("(turn 20)"); } } } /** 聴覚情報に対応する処理を行う */ protected void onHearMessage(SExp exp) throws Exception{ // messageの例 = "(hear 0 referee kick_off_l)" String speaker = exp.get(2).toString(); String content = exp.get(3).toString(); if(speaker.equals("referee")){ m_play_mode = content; } } /** 初期メッセージに対応する処理を行う */ protected void onInitMessage(SExp exp) throws Exception{ // messageの例 = "(init r 1 before_kick_off)" if(exp.size()!=4){ throw (new Exception("init message format error["+exp+"]")); } m_side = exp.get(1).toString(); m_number = Integer.parseInt(exp.get(2).toString()); m_play_mode = exp.get(3).toString(); } /** サッカーサーバのホスト名 */ private String m_server_ip = "localhost"; /** サッカーサーバの初期接続のポート番号 */ private int m_server_port = 6000; /** サッカーサーバのバージョンを指定する。空文字にするとサーバのデフォルトのバージョンで接続する */ private String m_server_version = "(version 8.99)"; /** UDBソケット */ private DatagramSocket m_socket = null; /** 味方のチーム名 */ protected String m_our_team_name = ""; /** 相手のチーム名 */ protected String m_opposing_team_name = "unknown"; /** 背番号(1〜11) */ protected int m_number = -1; /** 自軍のサイド */ protected String m_side = ""; /** 現在のゲーム状態 */ protected String m_play_mode = ""; /** オブジェクト(旗、ライン、ボール、相手、味方、ゴールなどの視覚的に見える物)の情報を取り扱うクラス */ static class Obj { /** オブジェクトの名称(b [ボール], f [旗], p [人], l [ライン], g [ゴール]など。この文字の後ろに情報が付加されて名称となる。) */ private String m_name = ""; /** オブジェクトの名称がS式の形式になっているときに、それぞれのアトムをパーツとして格納する。 */ private String[] m_name_parts = null; /** 観測者からの距離 */ private double m_distance = 0.0; /** 観測者からの相対的な角度 */ private double m_direction = 0.0; /** 絶対位置(コンストラクタからは設定されず、calcPositionをコールするとセットされる。) */ private Point2D.Double m_position = null; /** コンストラクタ */ public Obj(String name,double distance,double direction) throws Exception{ m_name = name; if(SExp.isSExp(m_name)){ SExp e = new SExp(m_name); m_name_parts = new String[e.size()]; for(int cnt=0;cnt flags = getObjects(exp,Pattern.compile("\\(f .*")); int count_flags = 0; double x = 0, y = 0; for(Obj flag : flags){ if(flag.getNamePartsSize()>=3){ double flag_x=Double.NaN,flag_y=Double.NaN; String border = flag.getNamePart(1); String side = flag.getNamePart(2); if(border.equals("t") || border.equals("b")){ flag_y = border.equals("t")?-39.0:39.0; if(side.equals("0")){ flag_x = 0.0; }else if(side.equals("r") && flag.getNamePartsSize()>=4){ flag_x = Double.parseDouble(flag.getNamePart(3)); }else if(side.equals("l") && flag.getNamePartsSize()>=4){ flag_x = -Double.parseDouble(flag.getNamePart(3)); } }else if(border.equals("r") || border.equals("l")){ flag_x = border.equals("r")?57.0:-57.0; if(side.equals("0")){ flag_y = 0; }else if(side.equals("t") && flag.getNamePartsSize()>=4){ flag_y = -Double.parseDouble(flag.getNamePart(3)); }else if(side.equals("b") && flag.getNamePartsSize()>=4){ flag_y = Double.parseDouble(flag.getNamePart(3)); } } if(Double.isNaN(flag_x)==false && Double.isNaN(flag_y)==false){ double flag_distance = flag.getDistance(); double flag_direction = flag.getDirection(); x += flag_x-flag_distance*Math.cos(d2r(my_direction+flag_direction)); y += flag_y-flag_distance*Math.sin(d2r(my_direction+flag_direction)); count_flags++; } } } if(count_flags == 0){ return null; } return new Point2D.Double(x/count_flags,y/count_flags); } /** 自分の向きを取得する。角度は、右(g r)のゴールの向きを0度として、右回転をプラス回転とする。 */ protected double getDirection(SExp exp) throws Exception{ SExp line_exp = exp.find(Pattern.compile("\\(l .*")); if(line_exp == null){ dbg_print("could not find my direction"); return 0; } Obj line = getObject(exp,line_exp.get(0).toString()); String type = line.getNamePart(1); double direction = line.getDirection()<0?(180+line.getDirection()):line.getDirection(); double my_direction = 0; if(type.equals("t")){ my_direction = -direction; }else if(type.equals("b")){ my_direction = 180-direction; }else if(type.equals("l")){ my_direction = -90-direction; }else if(type.equals("r")){ my_direction = 90-direction; } my_direction = normalize_degree(my_direction); return my_direction; } /** ボールに一番近いかをチェックする。 */ protected boolean isNearest(SExp exp,Obj ball,Vector our_players) throws Exception{ for(int cnt=0;cnt180){ degree -= 360; } return degree; } /** ゴールに向かってキックする */ protected String kick(SExp exp,Obj opposing_goal,double my_direction) throws Exception{ if(opposing_goal == null){ double desired_direction = m_side.equals("l")?0:180; return "(kick 80 "+normalize_degree(desired_direction-my_direction)+")"; }else{ return "(kick 100 "+opposing_goal.getDirection()+")"; } } /** 複数のオブジェクト情報を絶対位置を含めて取得する。 */ protected static Vector getObjects(SExp exp,Object name,Point2D.Double my_position,double my_direction) throws Exception{ Vector objs = getObjects(exp,name); for(Obj obj : objs){ obj.calcPosition(my_position,my_direction); } return objs; } /** 複数のオブジェクト情報を取得する。 */ protected static Vector getObjects(SExp exp,Object name) throws Exception{ Vector exps = exp.findAll(name); Vector objs = new Vector(); for(int cnt=0;cnt players = getObjects(exp,Pattern.compile("\\(p .*")); for(Obj player : players){ String team_name = player.getNamePart(1).split("\"")[1]; if(team_name.equals(m_our_team_name)==false){ return team_name; } } return ""; } /** ゲーム中であるかどうかを確認する。 */ protected boolean checkPlayingMode(){ return m_play_mode.startsWith("kick_off_"); } /** ゲームが停止しているかどうかを確認する。 */ protected boolean checkInitialMode(){ return m_play_mode.equals("before_kick_off") || m_play_mode.startsWith("goal_l") || m_play_mode.startsWith("goal_r"); } /** デバックプリント */ protected void dbg_print(String msg){ System.out.printf("[%s][%d] %s\n",m_our_team_name,m_number,msg); } /** 背番号からキックオフ時のプレイヤの位置を取得する。 */ protected Point2D.Double getKickOffPosition(int number){ double x; if(number == 1){ x = -50.0; }else if(number <= 5){ x = -40.0; }else if(number <= 9){ x = -20.0; }else if(number <= 10){ x = -1.0; }else{ x = -4.0; } double y; if(number == 1){ y = 0.0; }else if(number <= 9){ y = 10.0*((number-2)%4)-15.0; }else{ y = 15.0*((number-10)%2)-5.0; } return new Point2D.Double(x,y); } /** プレイヤの初期化(サーバーへの登録)を行う。 */ public void initialize(int number) throws Exception{ m_number = number; m_our_team_name = getClass().toString().split(" ")[1]; m_socket = new DatagramSocket(); if(m_number == 1){ send("(init "+m_our_team_name+" (goalie)"+m_server_version+")"); }else{ send("(init "+m_our_team_name+(m_server_version.length()==0?"":" ")+m_server_version+")"); } (new Thread(this)).start(); } /** サッカーサーバへコマンドを送信する。 */ protected void send(String command) throws Exception{ if(command.length() == 0){ return; } SExp exp = new SExp(command); String cmd = command+"\0"; InetSocketAddress address = new InetSocketAddress(m_server_ip,m_server_port); DatagramPacket packet = new DatagramPacket(cmd.getBytes(),cmd.length(),address); m_socket.send(packet); } /** サッカーサーバからメッセージを受信する。 */ protected String receive() throws Exception{ byte[] buf = new byte[4096]; DatagramPacket packet = new DatagramPacket(buf,buf.length); m_socket.receive(packet); m_server_port = packet.getPort(); String msg = new String(packet.getData(),0,packet.getLength()); return msg; } /** スレッド処理 */ public void run(){ try{ while(true){ String msg = receive(); SExp exp = new SExp(msg); String type = exp.get(0).toString(); if(type.equals("init")){ onInitMessage(exp); }else if(type.equals("see")){ onSeeMessage(exp); }else if(type.equals("hear")){ onHearMessage(exp); }else if(type.equals("warning")){ dbg_print("[WARNING] ["+msg+"]"); }else if(type.equals("error")){ dbg_print("[ERROR] ["+msg+"]"); }else{ // dbg_print("unknown received message ["+msg+"]"); } } }catch(Exception e){ e.printStackTrace(); } } /** メイン関数 */ public static void main(String[] args) throws Exception { for(int cnt=0;cnt<11;cnt++){ SamplePlayer0 players = new SamplePlayer0(); players.initialize(cnt+1); } } }