diff --git a/.gitignore b/.gitignore index e653fe1d..d09beb47 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,6 @@ grpc/python-v1/src/__pycache__/SamplePlayerAgent.cpython-310.pyc build/ cmake-build-debug/ cmake-build-release/ -cmake-build-debug-wsl/ \ No newline at end of file +cmake-build-debug-wsl/ +grpc/python-v1/__pycache__/ +grpc/python-v1/src/__pycache__/ \ No newline at end of file diff --git a/grpc/protos/service.proto b/grpc/protos/service.proto index 45568358..586b3431 100644 --- a/grpc/protos/service.proto +++ b/grpc/protos/service.proto @@ -813,9 +813,17 @@ message Action { HeliosSetPlay helios_set_play = 62; HeliosPenalty helios_penalty = 63; HeliosCommunicaion helios_communication = 64; + Bhv_NormalDribble bhv_normal_dribble = 65; } } +message Bhv_NormalDribble { + Vector2D target_point = 1; + float first_velocity = 2; + int32 n_turn = 3; + int32 n_dash = 4; +} + message Actions { repeated Action actions = 1; } diff --git a/grpc/python-v1/src/DM_WithBall.py b/grpc/python-v1/src/DM_WithBall.py index d15c9453..2e64c9a4 100644 --- a/grpc/python-v1/src/DM_WithBall.py +++ b/grpc/python-v1/src/DM_WithBall.py @@ -5,19 +5,23 @@ from pyrusgeom.soccer_math import * from pyrusgeom.geom_2d import * from src.GEN_Pass import GeneratorPass -from src.IBallAction import BallAction +from src.GEN_Dribble import GeneratorDribble +from src.IBallAction import ActionType, BallAction import time class WithBallDecisionMaker(IDecisionMaker): def __init__(self): self.pass_generator = GeneratorPass() + self.dribble_generator = GeneratorDribble() pass sum_time = 0 count = 0 def make_decision(self, agent: IAgent): start_time = time.time() - candidate_actions: list[BallAction] = self.pass_generator.generate(agent, 0) + candidate_actions: list[BallAction] = [] + # candidate_actions = self.pass_generator.generate(agent, 0) + candidate_actions += self.dribble_generator.generator(agent) if len(candidate_actions) == 0: agent.add_action(pb2.Action(body_hold_ball=pb2.Body_HoldBall())) @@ -37,7 +41,7 @@ def make_decision(self, agent: IAgent): if candidate.score > best_score: best_score = candidate.score best_action = candidate - break + break end_time = time.time() WithBallDecisionMaker.sum_time += end_time - start_time WithBallDecisionMaker.count += 1 @@ -46,6 +50,17 @@ def make_decision(self, agent: IAgent): agent.add_action(pb2.Action(body_hold_ball=pb2.Body_HoldBall())) return + if best_action.actionType == ActionType.DRIBBLE: + agent.add_action( + pb2.Action( + bhv_normal_dribble=pb2.Bhv_NormalDribble( + target_point=pb2.Vector2D(x=best_action.targetBallPos.x(), y=best_action.targetBallPos.y()), + first_velocity=best_action.firstVelocity.r(), + n_turn=best_action.n_turn, + n_dash=best_action.n_dash, + ) + ) + ) agent.add_action(pb2.Action(body_smart_kick=pb2.Body_SmartKick( target_point=pb2.Vector2D(x=best_action.targetBallPos.x(), y=best_action.targetBallPos.y()), first_speed=best_action.firstVelocity.r(), diff --git a/grpc/python-v1/src/GEN_Dribble.py b/grpc/python-v1/src/GEN_Dribble.py new file mode 100644 index 00000000..bb945a11 --- /dev/null +++ b/grpc/python-v1/src/GEN_Dribble.py @@ -0,0 +1,283 @@ +from pyrusgeom.geom_2d import * +import pyrusgeom.soccer_math as smath +from src.IAgent import IAgent +from src.IBallAction import ActionType, DribbleAction + +from src.IBallActionGenerator import BallActionGenerator +from src.Tools import Tools +from service_pb2 import WorldModel + +debug_dribble = False +max_dribble_time = 0 + + +class GeneratorDribble(BallActionGenerator): + def __init__(self): + super().__init__() + self.index = 0 + self.candidates = [] + + def generator(self, agent: IAgent): + global max_dribble_time + self.generate_simple_dribble(agent) + + # if debug_dribble: + # for dribble in self.debug_list: + # if dribble[2]: + # log.sw_log().dribble().add_message( dribble[1].x(), dribble[1].y(), '{}'.format(dribble[0])) + # log.sw_log().dribble().add_circle( cicle=Circle2D(dribble[1], 0.2), + # color=Color(string='green')) + # else: + # log.sw_log().dribble().add_message( dribble[1].x(), dribble[1].y(), '{}'.format(dribble[0])) + # log.sw_log().dribble().add_circle( cicle=Circle2D(dribble[1], 0.2), + # color=Color(string='red')) + # log.sw_log().dribble().add_text( 'time:{} max is {}'.format(end_time - start_time, max_dribble_time)) + return self.candidates + + def generate_simple_dribble(self, agent: IAgent): + wm = agent.wm + angle_div = 16 + angle_step = 360.0 / angle_div + + sp = agent.serverParams + ptype = agent.get_type(wm.self.type_id) + + my_first_speed = Tools.vector2d_message_to_vector2d(wm.self.velocity).r() + + for a in range(angle_div): + dash_angle = AngleDeg(wm.self.body_direction + (angle_step * a)) + + if wm.self.position.x < 16.0 and dash_angle.abs() > 100.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( '#dash angle:{} cancel is not safe1'.format(dash_angle)) + continue + + if wm.self.position.x < -36.0 and abs(wm.self.position.y) < 20.0 and dash_angle.abs() > 45.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( '#dash angle:{} cancel is not safe2'.format(dash_angle)) + continue + + n_turn = 0 + + my_speed = my_first_speed * ptype.player_decay + dir_diff = AngleDeg(angle_step * a).abs() + + while dir_diff > 10.0: + dir_diff -= Tools.effective_turn(sp.max_moment, my_speed, ptype.inertia_moment) + if dir_diff < 0.0: + dir_diff = 0.0 + my_speed *= ptype.player_decay + n_turn += 1 + + if n_turn >= 3: + continue + + if angle_step * a < 180.0: + dash_angle -= dir_diff + else: + dash_angle += dir_diff + # if debug_dribble: + # log.sw_log().dribble().add_text( '#dash angle:{} turn:{}'.format(dash_angle, n_turn)) + self.simulate_kick_turns_dashes(agent, dash_angle, n_turn) + + def simulate_kick_turns_dashes(self, agent: IAgent, dash_angle, n_turn): + wm = agent.wm + + max_dash = 8 + min_dash = 2 + + self_cache = [] + + self.create_self_cache(agent, dash_angle, n_turn, max_dash, self_cache) + # if debug_dribble: + # log.sw_log().dribble().add_text( '##self_cache:{}'.format(self_cache)) + sp = agent.serverParams + ptype = agent.get_type(wm.self.type_id) + + # trap_rel = Vector2D.polar2vector(ptype.playerSize() + ptype.kickableMargin() * 0.2 + SP.ball_size(), dash_angle) + trap_rel = Vector2D.polar2vector(ptype.player_size + ptype.kickable_margin * 0.2 + 0, dash_angle) + + max_x = sp.pitch_half_length - 1.0 + max_y = sp.pitch_half_width - 1.0 + + ball_pos = Tools.vector2d_message_to_vector2d(wm.ball.position) + ball_vel = Tools.vector2d_message_to_vector2d(wm.ball.velocity) + + for n_dash in range(max_dash, min_dash - 1, -1): + self.index += 1 + ball_trap_pos:Vector2D = self_cache[n_turn + n_dash] + trap_rel + + if ball_trap_pos.abs_x() > max_x or ball_trap_pos.abs_y() > max_y: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # '#index:{} target:{} our of field'.format(self.index, ball_trap_pos)) + # self.debug_list.append((self.index, ball_trap_pos, False)) + continue + + term = (1.0 - pow(sp.ball_decay, 1 + n_turn + n_dash ) ) / (1.0 - sp.ball_decay) + first_vel: Vector2D = (ball_trap_pos - ball_pos) / term + kick_accel: Vector2D = first_vel - ball_vel + kick_power = kick_accel.r() / wm.self.kick_rate + + if kick_power > sp.max_power or kick_accel.r2() > pow(sp.ball_accel_max, 2) or first_vel.r2() > pow( + sp.ball_speed_max, 2): + # if debug_dribble: + # log.sw_log().dribble().add_text( + # '#index:{} target:{} need more power, power:{}, accel:{}, vel:{}'.format( + # self.index, ball_trap_pos, kick_power, kick_accel, first_vel)) + # self.debug_list.append((self.index, ball_trap_pos, False)) + continue + + if (ball_pos + first_vel).dist2(self_cache[0]) < pow(ptype.player_size + sp.ball_size + 0.1, 2): + # if debug_dribble: + # log.sw_log().dribble().add_text( + # '#index:{} target:{} in body, power:{}, accel:{}, vel:{}'.format( + # self.index, ball_trap_pos, kick_power, kick_accel, first_vel)) + # self.debug_list.append((self.index, ball_trap_pos, False)) + continue + + candidate = DribbleAction() + candidate.actionType = ActionType.DRIBBLE + candidate.initBallPos = Tools.vector2d_message_to_vector2d(wm.ball.position) + candidate.targetBallPos = ball_trap_pos + candidate.targetUnum = wm.self.uniform_number + candidate.firstVelocity = first_vel + candidate.index = self.index + candidate.dribble_steps = n_turn + n_dash + 1 + candidate.n_turn = n_turn + candidate.n_dash = n_dash + candidate.evaluate() + self.candidates.append(candidate) + # if debug_dribble: + # log.sw_log().dribble().add_text( + # '#index:{} target:{}, power:{}, accel:{}, vel:{} OK'.format( + # self.index, ball_trap_pos, kick_power, kick_accel, first_vel)) + # self.debug_list.append((self.index, ball_trap_pos, True)) + # else: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # '#index:{} target:{}, power:{}, accel:{}, vel:{} Opponent catch it'.format( + # self.index, ball_trap_pos, kick_power, kick_accel, first_vel)) + # self.debug_list.append((self.index, ball_trap_pos, False)) + + def create_self_cache(self, agent: IAgent, dash_angle, n_turn, n_dash, self_cache): + wm = agent.wm + sp = agent.serverParams + ptype = agent.get_type(wm.self.type_id) + + self_cache.clear() + + # stamina_model = wm.self().stamina_model() + + my_pos = Tools.vector2d_message_to_vector2d(wm.self.position) + my_vel = Tools.vector2d_message_to_vector2d(wm.self.velocity) + + my_pos += my_vel + my_vel *= ptype.player_decay + + self_cache.append(Vector2D(my_pos)) + + for i in range(n_turn): + my_pos += my_vel + my_vel *= ptype.player_decay + self_cache.append(Vector2D(my_pos)) + # stamina_model.simulate_waits(ptype, 1 + n_turn) + + unit_vec = Vector2D.polar2vector(1.0, dash_angle) + + for i in range(n_dash): + # available_stamina = max(0.0, stamina_model.stamina() - sp.recover_dec_thr - 300.0) + # dash_power = min(available_stamina, sp.max_dash_power) + dash_power = sp.max_dash_power + dash_accel = unit_vec.set_length_vector(dash_power * ptype.dash_power_rate * sp.effort_init) + + my_vel += dash_accel + my_pos += my_vel + my_vel *= ptype.player_decay + + # stamina_model.simulate_dash(ptype, dash_power) + self_cache.append(Vector2D(my_pos)) + + def check_opponent(self, agent: IAgent, ball_trap_pos: Vector2D, dribble_step: int): + wm = agent.wm + sp = agent.serverParams + ball_move_angle:AngleDeg = (ball_trap_pos - Tools.vector2d_message_to_vector2d(wm.ball.position)).th() + + for o in range(12): + opp = wm.their_players_dict[o] + if opp is None or opp.uniform_number == 0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is ghost".format(o)) + continue + + if opp.dist_from_self > 20.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is far".format(o)) + continue + + ptype = agent.get_type(opp.type_id) + + control_area = (sp.catchable_area + if opp.is_goalie + and ball_trap_pos.x() > sp.their_penalty_area_line_x + and ball_trap_pos.abs_y() < sp.penalty_area_half_width + else ptype.kickable_area) + + opp_pos = Tools.inertia_point(Tools.vector2d_message_to_vector2d(opp.position), opp.velocity, dribble_step, ptype.player_decay) + + ball_to_opp_rel = (Tools.vector2d_message_to_vector2d(opp.position) - Tools.vector2d_message_to_vector2d(wm.ball.position)).rotated_vector(-ball_move_angle) + + if ball_to_opp_rel.x() < -4.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is behind".format(o)) + continue + + target_dist = opp_pos.dist(ball_trap_pos) + + if target_dist - control_area < 0.001: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} Catch, ball will be in his body".format(o)) + return False + + dash_dist = target_dist + dash_dist -= control_area * 0.5 + dash_dist -= 0.2 + n_dash = ptype.cycles_to_reach_distance(dash_dist) + + n_turn = 1 if opp.body_count() > 1 else Tools.predict_player_turn_cycle(ptype, + opp.body_direction, + Tools.vector2d_message_to_vector2d(opp.velocity).r(), + target_dist, + (ball_trap_pos - opp_pos).th(), + control_area, + True) + + n_step = n_turn + n_dash if n_turn == 0 else n_turn + n_dash + 1 + + bonus_step = 0 + if ball_trap_pos.x() < 30.0: + bonus_step += 1 + + if ball_trap_pos.x() < 0.0: + bonus_step += 1 + + if opp.is_tackling(): + bonus_step = -5 + + if ball_to_opp_rel.x() > 0.5: + bonus_step += smath.bound(0, opp.pos_count(), 8) + else: + bonus_step += smath.bound(0, opp.pos_count(), 4) + + if n_step - bonus_step <= dribble_step: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # "###OPP {} catch n_step:{}, dr_step:{}, bonas:{}".format(o, n_step, dribble_step, + # bonus_step)) + return False + # else: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # "###OPP {} can't catch n_step:{}, dr_step:{}, bonas:{}".format(o, n_step, dribble_step, + # bonus_step)) + return True diff --git a/grpc/python-v1/src/IBallAction.py b/grpc/python-v1/src/IBallAction.py index dd8ebeeb..ebe342bb 100644 --- a/grpc/python-v1/src/IBallAction.py +++ b/grpc/python-v1/src/IBallAction.py @@ -46,7 +46,107 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"ActionType: {self.actionType}, score: {self.score}, targetBallPos: {self.targetBallPos}, firstVelocity: {self.firstVelocity}, targetVelocity: {self.targetVelocity}, targetDirection: {self.targetDirection}, success: {self.success}" + + +class DribbleAction(BallAction): + def __init__(self) -> None: + super().__init__() + self.dribble_steps: int = 0 + self.n_turn: int = 0 + self.n_dash: int = 0 + def check_possibility(self, agent: IAgent) -> None: + wm = agent.wm + sp = agent.serverParams + ball_trap_pos = self.targetBallPos + dribble_step = self.dribble_steps + + ball_move_angle:AngleDeg = (ball_trap_pos - Tools.vector2d_message_to_vector2d(wm.ball.position)).th() + + for o in range(12): + opp = wm.their_players_dict[o] + if opp is None or opp.uniform_number == 0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is ghost".format(o)) + continue + + if opp.dist_from_self > 20.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is far".format(o)) + continue + + ptype = agent.get_type(opp.type_id) + + control_area = (sp.catchable_area + if opp.is_goalie + and ball_trap_pos.x() > sp.their_penalty_area_line_x + and ball_trap_pos.abs_y() < sp.penalty_area_half_width + else ptype.kickable_area) + + opp_pos = Tools.inertia_point(Tools.vector2d_message_to_vector2d(opp.position), + Tools.vector2d_message_to_vector2d(opp.velocity), + dribble_step, + ptype.player_decay) + + ball_to_opp_rel = (Tools.vector2d_message_to_vector2d(opp.position) - Tools.vector2d_message_to_vector2d(wm.ball.position)).rotated_vector(-ball_move_angle) + + if ball_to_opp_rel.x() < -4.0: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} is behind".format(o)) + continue + + target_dist = opp_pos.dist(ball_trap_pos) + + if target_dist - control_area < 0.001: + # if debug_dribble: + # log.sw_log().dribble().add_text( "###OPP {} Catch, ball will be in his body".format(o)) + return False + + dash_dist = target_dist + dash_dist -= control_area * 0.5 + dash_dist -= 0.2 + n_dash = Tools.cycles_to_reach_distance(dash_dist, ptype.real_speed_max) + + n_turn = 1 if opp.body_direction_count > 1 else Tools.predict_player_turn_cycle(sp, + ptype, + opp.body_direction, + Tools.vector2d_message_to_vector2d(opp.velocity).r(), + target_dist, + (ball_trap_pos - opp_pos).th(), + control_area, + True) + + n_step = n_turn + n_dash if n_turn == 0 else n_turn + n_dash + 1 + + bonus_step = 0 + if ball_trap_pos.x() < 30.0: + bonus_step += 1 + + if ball_trap_pos.x() < 0.0: + bonus_step += 1 + + if opp.is_tackling: + bonus_step = -5 + + if ball_to_opp_rel.x() > 0.5: + bonus_step += smath.bound(0, opp.pos_count, 8) + else: + bonus_step += smath.bound(0, opp.pos_count, 4) + + if n_step - bonus_step <= dribble_step: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # "###OPP {} catch n_step:{}, dr_step:{}, bonas:{}".format(o, n_step, dribble_step, + # bonus_step)) + self.success = False + return + # else: + # if debug_dribble: + # log.sw_log().dribble().add_text( + # "###OPP {} can't catch n_step:{}, dr_step:{}, bonas:{}".format(o, n_step, dribble_step, + # bonus_step)) + self.success = True + class PassAction(BallAction): def __init__(self) -> None: diff --git a/src/grpc/grpc_agent.cpp b/src/grpc/grpc_agent.cpp index f7d40f9c..4d0c204d 100644 --- a/src/grpc/grpc_agent.cpp +++ b/src/grpc/grpc_agent.cpp @@ -45,6 +45,8 @@ #include "player/bhv_basic_move.h" #include "player/setplay/bhv_set_play.h" #include "player/bhv_penalty_kick.h" +#include "player/planner/dribble.h" +#include "player/planner/bhv_normal_dribble.h" #include #include @@ -902,7 +904,9 @@ void GrpcAgent::getAction(rcsc::PlayerAgent * agent) const{ } case Action::kHeliosSetPlay: { + LOG("set play"); Bhv_SetPlay().execute( agent ); + LOG("set play done"); break; } case Action::kHeliosPenalty: @@ -916,6 +920,25 @@ void GrpcAgent::getAction(rcsc::PlayerAgent * agent) const{ break; } + case Action::kBhvNormalDribble: + { + const auto& bhvNormalDribble = action.bhv_normal_dribble(); + const auto& targetPoint = GrpcAgent::convertVector2D(bhvNormalDribble.target_point()); + auto dribble = rcsc::Dribble(agent->world().self().unum(),targetPoint, bhvNormalDribble.first_velocity(), 1, bhvNormalDribble.n_turn(), bhvNormalDribble.n_dash(), "shortDribble"); + std::cout << "Dribble" << ", " + << agent->world().self().unum() << ", " + << targetPoint << ", " + << bhvNormalDribble.first_velocity() << ", " + << 1 << ", " + << bhvNormalDribble.n_turn() << ", " + << bhvNormalDribble.n_dash() << ", " + << "shortDribble" << std::endl; + + Bhv_NormalDribble(dribble).execute(agent); + body_action_done++; + break; + } + // HeliosChainAction helios_chain_action = 59; default: {