├── lib ├── __init__.py ├── option.pyc ├── state.pyc ├── __init__.pyc ├── la_class.pyc ├── state.pxd ├── lib_log_analyzer.pxd ├── distribution3dplot.py ├── option.py ├── state.py ├── lib_log_analyzer.py ├── la_class.pxd └── la_class.py ├── calculation ├── __init__.py ├── dribble_count.pxd ├── offside_line.pxd ├── through_pass.pxd ├── calculate.pxd ├── kick_distribution.pxd ├── pass_check.pxd ├── shoot.pxd ├── nearest_player.py ├── dribble_count.py ├── offside_line.py ├── kick_sequence.pxd ├── through_pass.py ├── shoot.py ├── pass_probability.py ├── kick_distribution.py ├── pass_check.py ├── calculate.py └── kick_sequence.py ├── extraction ├── __init__.py ├── hetero.pxd ├── filename_split.pxd ├── get_kick.pxd ├── rcl_reader.pxd ├── rcg_reader.pxd ├── extract.pxd ├── referee.pxd ├── get_dash.py ├── get_tackle.pxd ├── hetero.py ├── referee.py ├── filename_split.py ├── rcl_reader.py ├── get_kick.py ├── get_tackle.py ├── kick_txt.py ├── extract.py └── rcg_reader.py ├── .gitignore ├── requirements.txt ├── clean.sh ├── loganalyzer3 ├── setup.py ├── main.py ├── loop.sh ├── README.md └── ChangeLog /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /calculation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extraction/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/option.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opusymcomp/loganalyzer3/HEAD/lib/option.pyc -------------------------------------------------------------------------------- /lib/state.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opusymcomp/loganalyzer3/HEAD/lib/state.pyc -------------------------------------------------------------------------------- /lib/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opusymcomp/loganalyzer3/HEAD/lib/__init__.pyc -------------------------------------------------------------------------------- /lib/la_class.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opusymcomp/loganalyzer3/HEAD/lib/la_class.pyc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rcg* 2 | *.rcl* 3 | *.csv 4 | *__pycache__* 5 | *.pyc 6 | *.c 7 | *.so 8 | *.eps 9 | *.pdf 10 | *.png 11 | *.svg 12 | build/ 13 | *idea/ -------------------------------------------------------------------------------- /calculation/dribble_count.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | from lib cimport la_class 5 | cdef void countDribble( la_class.WorldModel wm, int cycle, la_class.Feature feat ) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2021.10.8 2 | cycler==0.10.0 3 | Cython==0.29.24 4 | kiwisolver==1.3.2 5 | matplotlib==3.2.2 6 | numpy==1.22.0 7 | Pillow==9.0.1 8 | pyparsing==2.4.7 9 | python-dateutil==2.8.2 10 | six==1.16.0 11 | -------------------------------------------------------------------------------- /extraction/hetero.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(tmp=cython.list) 8 | cdef void getHetero( str line, int h_id, la_class.ServerParam sp ) -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # clean binaries created by cython 4 | DIR=`dirname $0` 5 | 6 | find ${DIR} -type f -name *cpython-*-linux-gnu.so -print0 | xargs -0 rm -rf 7 | find ${DIR} -type f -name *.c -print0 | xargs -0 rm -rf 8 | rm -r ./build/ 9 | -------------------------------------------------------------------------------- /calculation/offside_line.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(off_l=cython.int, off_r=cython.int, side=cython.str) 8 | cdef void calcOffsideLine( list args, la_class.WorldModel wm ) -------------------------------------------------------------------------------- /extraction/filename_split.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | @cython.locals(left=cython.str, right=cython.str, lpoint=cython.str, rpoint=cython.str, getpoint=cython.int, lostpoint=cython.int) 7 | cdef list splitFileName( str filename, str logname, str team ): -------------------------------------------------------------------------------- /calculation/through_pass.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(last_kicked_cycle=cython.int) 8 | cdef bint isThroughPass( la_class.WorldModel wm, int cycle ) 9 | 10 | cdef void countThroughPass( la_class.WorldModel wm, int cycle, la_class.Feature feat ) 11 | -------------------------------------------------------------------------------- /extraction/get_kick.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | cdef bint checkKick( la_class.WorldModel wm, int unum, str side ): 8 | 9 | @cython.locals(num_kick=cython.int, kick_side_l=cython.bint, kick_side_r=cython.bint) 10 | cdef int isKick( la_class.WorldModel wm, int cycle ) 11 | -------------------------------------------------------------------------------- /calculation/calculate.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(kick_count=cython.int, direction=cython.str, l_disconnected_player=cython.int, r_disconnected_player=cython.int) 8 | cdef void analyzeLog(list args, la_class.WorldModel[:] wm, la_class.ServerParam sp, la_class.Feature feat) -------------------------------------------------------------------------------- /calculation/kick_distribution.pxd: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/env python 3 | # cython: language_level=3 4 | 5 | import cython 6 | from lib cimport la_class 7 | 8 | @cython.locals(degree=cython.double) 9 | cdef void saveKickDistribution( la_class.Feature feat ) 10 | 11 | # cdef printKickDistribution( la_class.ServerParam sp, la_class.Feature feat ): 12 | -------------------------------------------------------------------------------- /lib/state.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | from lib cimport la_class as la 5 | 6 | cdef bint isGoalie( int cycle, int unum, str side, la.WorldModel[:] wm ) 7 | 8 | cdef bint isDead( int cycle, int unum, str side, la.WorldModel[:] wm ) 9 | 10 | cdef bint checkTackle( str state ) 11 | 12 | cdef bint checkKick( la.WorldModel[:] wm, int unum, str side ) -------------------------------------------------------------------------------- /extraction/rcl_reader.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(move_teamname=cython.str, move_cycle=cython.int, move_player=cython.int, seq=cython.int) 8 | cdef void getInitialPosition( str line, la_class.WorldModel wm ) 9 | 10 | @cython.locals(teamname=cython.str, unum=cython.int) 11 | cdef void getAction( str line, la_class.WorldModel wm ) -------------------------------------------------------------------------------- /extraction/rcg_reader.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | from lib cimport la_class 5 | 6 | 7 | cdef void getInformation( str[:] tmp_line, la_class.WorldModel wm ) 8 | 9 | cdef void getBallInformation( str[:] tmp_line, la_class.WorldModel wm ) 10 | 11 | cdef void getLeftTeamInformation( str[:] tmp_line, la_class.WorldModel wm ) 12 | 13 | cdef void getRightTeamInformation( str[:] tmp_line, la_class.WorldModel wm ) -------------------------------------------------------------------------------- /calculation/pass_check.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | @cython.locals(direction=cython.double) 8 | cdef str countKick( la_class.WorldModel wm, int cycle, la_class.Feature feat ) 9 | 10 | cdef void countPass( la_class.WorldModel wm, int cycle, str direction, la_class.Feature feat ): 11 | 12 | @cython.locals(last_kicked_cycle=cython.int, radian=cython.double, degree=cython.double) 13 | cdef str getPassRoute( la_class.WorldModel wm, int cycle ): 14 | -------------------------------------------------------------------------------- /extraction/extract.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import os 5 | import cython 6 | 7 | from lib cimport la_class 8 | 9 | @cython.locals(cycle=cython.int, current_play_mode=cython.str, hetero_id=cython.int, tmp_line=cython.list, tmp_result=cython.list) 10 | cdef void extractRcg( list args, la_class.WorldModel[:] wm, la_class.SeverParam sp, la_class.Feature feature) 11 | 12 | @cython.locals(filename=cython.str, cycle=cython.int) 13 | cdef void extractRcl(list args, la_class.WorldModel[:] wm, la_class.SeverParam sp) 14 | -------------------------------------------------------------------------------- /calculation/shoot.pxd: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/env python 3 | # cython: language_level=3 4 | 5 | # implemented by Hori 6 | 7 | import cython 8 | from lib cimport la_class 9 | 10 | @cython.locals(x=cython.double, y=cython.double, xv=cython.double, yv=cython.double) 11 | cdef bint isOurShoot( la_class.WorldModel wm, la_class.ServerParam sp, int cycle, str side ) 12 | 13 | @cython.locals(x=cython.double, y=cython.double, xv=cython.double, yv=cython.double) 14 | cdef bint isOppShoot( la_class.WorldModel wm, la_class.ServerParam sp, int cycle, str side ) 15 | -------------------------------------------------------------------------------- /extraction/referee.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | # --- format --- # 8 | # goal [ cycle, team( l or r ) ] 9 | 10 | @cython.locals(our_yellow_card=cython.int, tmp=cython.list) 11 | cdef int countOurYellowCard( la_class.WorldModel wm, str team ) 12 | 13 | @cython.locals(opp_yellow_card=cython.int, tmp=cython.list) 14 | cdef int countOppYellowCard( la_class.WorldModel wm, str team ) 15 | 16 | cdef str getCurrentPlayMode( str line ) 17 | 18 | cdef void setPlayMode( str current_play_mode, la_class.WorldModel wm ) 19 | 20 | cdef void sayMessage( str line, la_class.WorldModel wm ): 21 | -------------------------------------------------------------------------------- /extraction/get_dash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | 7 | def isDash( wm: list, cycle: cython.int ) -> cython.bint: 8 | for unum in range( 11 ): 9 | if ( wm[cycle + 1].last_kicker_unum == wm[cycle].last_kicker_unum ): 10 | if cycle in range(wm[cycle].last_kicked_cycle , wm[cycle+1].last_kicked_cycle ): 11 | if ( "(dash" in wm[cycle].l.player[unum].action and wm[cycle+1].last_kicker_unum == unum ): 12 | return True 13 | elif ( "(dash" in wm[cycle].r.player[unum].action and wm[cycle+1].last_kicker_unum == unum ): 14 | return True 15 | return False -------------------------------------------------------------------------------- /extraction/get_tackle.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | from lib cimport la_class 6 | 7 | 8 | cdef bint checkTackle( str state ) 9 | 10 | @cython.locals(all_tackle=cython.int) 11 | cdef int countOurTackle( la_class.WorldModel wm, str team ) 12 | 13 | @cython.locals(all_tackle=cython.int) 14 | cdef int countOppTackle( la_class.WorldModel wm, str team ): 15 | 16 | @cython.locals(success_tackle=cython.int) 17 | cdef int countOurSuccessTackle( la_class.WorldModel wm, la_class.WorldModel next_wm, str team ): 18 | 19 | @cython.locals(success_tackle=cython.int) 20 | cdef int countOppSuccessTackle( la_class.WorldModel wm, la_class.WorldModel next_wm, str team ): 21 | -------------------------------------------------------------------------------- /loganalyzer3: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ------------------------------------------------------ # 4 | # if you add export PATH="/path/to/loganalyzer3:$PATH" 5 | # you can use loganalyzer3 anywhere 6 | # 7 | # Examples of Execution 8 | # loganalyzer3 /path/to/dir --team HELIOS_base 9 | # loganalyzer3 /path/to/file --side l --debug 10 | # ------------------------------------------------------ # 11 | 12 | echo "LogAnalyzer3" 13 | echo "© 2018 Osaka Prefecture University Symbiotic Computing Lab." 14 | 15 | DIR=`dirname $0` 16 | if [[ "$*" =~ --debug ]]; then 17 | opt="" 18 | while [ $# -gt 0 ] 19 | do 20 | case $1 in 21 | --debug) 22 | shift 1 23 | ;; 24 | *) 25 | opt="${opt} ${1}" 26 | shift 1 27 | ;; 28 | esac 29 | done 30 | python -O ${DIR}/main.py ${opt} 31 | else 32 | python ${DIR}/main.py $* 33 | fi 34 | -------------------------------------------------------------------------------- /calculation/nearest_player.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def vec_dist( pos1, pos2 ): 4 | 5 | return math.sqrt( pow( pos1[0] - pos2[0], 2.0 ) + pow( pos1[1] - pos2[1], 2.0 ) ) 6 | 7 | ##################################### 8 | 9 | def get_nearest_player( ball_pos, player_pos ): 10 | 11 | min_dist = 10000000.0; 12 | nearest_player = -1 13 | 14 | for i in range( len( player_pos ) ): 15 | tmp_dist = vec_dist( ball_pos, player_pos[i] ) 16 | if tmp_dist < min_dist: 17 | nearest_player = i#player_pos[i] 18 | 19 | return nearest_player 20 | 21 | ##################################### 22 | 23 | def nearestPlayerFromBall( ball, pos_l, pos_r ): 24 | 25 | nearest_list = [] 26 | for i in range( len( ball ) ): 27 | tmp = [] 28 | tmp.append( get_nearest_player( ball[i], pos_l[i] ) ) 29 | tmp.append( get_nearest_player( ball[i], pos_r[i] ) ) 30 | nearest_list.append( tmp ) 31 | 32 | return nearest_list 33 | -------------------------------------------------------------------------------- /calculation/dribble_count.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from extraction import get_dash as gd 7 | from lib import la_class 8 | 9 | def countDribble( wm: list, cycle: cython.int, feat: la_class.Feature ) -> None: 10 | if ( wm[cycle + 1].last_kicker_unum == wm[cycle].last_kicker_unum ): 11 | if ( wm[cycle].dominate_side == feat.target_team ): 12 | if ( gd.isDash( wm, cycle ) ): 13 | if ( not __debug__ ): 14 | print ("our_dribble",wm[cycle].last_kicker_unum + 1, \ 15 | "cycle", wm[cycle+1].last_kicked_cycle ) 16 | feat.our_dribble += 1 17 | else: 18 | if ( gd.isDash( wm, cycle ) ): 19 | if ( not __debug__ ): 20 | print ("opp_dribble",wm[cycle].last_kicker_unum + 1, \ 21 | "cycle", wm[cycle+1].last_kicked_cycle ) 22 | feat.opp_dribble += 1 23 | -------------------------------------------------------------------------------- /lib/lib_log_analyzer.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | from lib cimport la_class 5 | 6 | # index c 7 | 8 | 9 | cdef double calcDist( la_class.Position pos1, la_class.Position pos2 ) 10 | 11 | cdef double calcDistC( double x1, double y1, double x2, double y2 ) 12 | 13 | cdef double calcRadian( la_class.Position pos1, la_class.Position pos2 ) 14 | 15 | cdef double calcRadianC( double x1, double y1, double x2, double y2 ) 16 | 17 | cdef double changeRadianToDegree(double radian) 18 | 19 | cdef int countPlayOn( int cycle1, int cycle2, list situation ) 20 | 21 | # index g 22 | 23 | cdef str getFileName( str data ) 24 | 25 | cdef int getResult( la_class.Feature feat ) 26 | 27 | cdef str getTeamName( str filename, str side ) 28 | 29 | # index i 30 | 31 | cdef bint isPlayOn( int cycle, list situation ) 32 | 33 | cdef bint isSameCycle( int now_count, int pre_count ) 34 | 35 | # index s 36 | 37 | cdef str selectTargetTeam( list args, str filename ) 38 | 39 | cdef list sortPlayerUnumFromPos( la_class.Position[:] player_list, la_class.Position target_pos ) 40 | -------------------------------------------------------------------------------- /calculation/offside_line.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | import argparse 6 | 7 | from lib import state 8 | 9 | def calcOffsideLine( args: argparse, wm: list ) -> None: 10 | 11 | off_l: cython.double = 0.0 12 | off_r: cython.double = 0.0 13 | 14 | for cycle in range( args.start_cycle, args.end_cycle+1 ): 15 | for unum in range( 11 ): 16 | if ( off_l > wm[ cycle - args.start_cycle ].l.player[unum].pos.x ): 17 | side = "l" 18 | if ( not state.isGoalie( cycle - args.start_cycle, unum, side, wm ) ): 19 | off_l = wm[cycle - args.start_cycle].l.player[unum].pos.x 20 | 21 | if ( off_r < wm[ cycle - args.start_cycle ].r.player[unum].pos.x ): 22 | side = "r" 23 | if ( not state.isGoalie( cycle - args.start_cycle, unum, side, wm ) ): 24 | off_r = wm[cycle - args.start_cycle].r.player[unum].pos.x 25 | 26 | wm[cycle - args.start_cycle].l.offsideLineX = off_l 27 | wm[cycle - args.start_cycle].r.offsideLineX = off_r 28 | 29 | off_l = 0.0 30 | off_r = 0.0 31 | -------------------------------------------------------------------------------- /extraction/hetero.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | 8 | 9 | def getHetero( line: str, h_id: cython.int, sp: la_class.ServerParam ) -> None: 10 | 11 | tmp: list = line.split(")(") 12 | 13 | sp.hetero_params[h_id].player_speed_max = float( tmp[1].split()[1] ) 14 | sp.hetero_params[h_id].stamina_inc_max = float( tmp[2].split()[1] ) 15 | sp.hetero_params[h_id].player_decay = float( tmp[3].split()[1] ) 16 | sp.hetero_params[h_id].inertia_moment = float( tmp[4].split()[1] ) 17 | sp.hetero_params[h_id].dash_power_rate = float( tmp[5].split()[1] ) 18 | sp.hetero_params[h_id].player_size = float( tmp[6].split()[1] ) 19 | sp.hetero_params[h_id].kick_land = float( tmp[8].split()[1] ) 20 | sp.hetero_params[h_id].extra_stamina = float( tmp[9].split()[1] ) 21 | sp.hetero_params[h_id].effort_max = float( tmp[10].split()[1] ) 22 | sp.hetero_params[h_id].effort_min = float( tmp[11].split()[1] ) 23 | sp.hetero_params[h_id].kick_power_rate = float( tmp[12].split()[1] ) 24 | sp.hetero_params[h_id].foul_detect_probability = float( tmp[13].split()[1] ) 25 | sp.hetero_params[h_id].catchable_area_l_stretch = float( tmp[14].split()[1].strip("))") ) 26 | -------------------------------------------------------------------------------- /lib/distribution3dplot.py: -------------------------------------------------------------------------------- 1 | from mpl_toolkits.mplot3d import Axes3D 2 | import matplotlib.pyplot as plt 3 | import math 4 | import sys 5 | import numpy as np 6 | 7 | def Plot3d( date ): 8 | 9 | x = [] 10 | y = [] 11 | z = [] 12 | 13 | fig = plt.figure() 14 | ax = Axes3D( fig ) 15 | txtname = date + '_kick_dist.txt' 16 | 17 | for l in open( txtname, 'r' ): 18 | tmp = l.split(); 19 | ax.plot3D( [float( tmp[0] ), float( tmp[0] )], [float( tmp[1] ), float( tmp[1] )], [0, float( tmp[2] )], "red" ) 20 | x.append( float( tmp[0] ) ) 21 | y.append( float( tmp[1] ) ) 22 | z.append( float( tmp[2] ) ) 23 | 24 | ax.plot3D( [0,0], [34,-34], [0,0], 'g' ) 25 | ax.plot3D( [52.5,52.5], [34,-34], [0,0], 'g' ) 26 | ax.plot3D( [-52.5,-52.5], [34,-34], [0,0], 'g' ) 27 | ax.plot3D( [52.5,-52.5], [34,34], [0,0], 'g' ) 28 | ax.plot3D( [52.5,-52.5], [-34,-34], [0,0], 'g' ) 29 | ax.plot3D( [-36,-36], [20.16,-20.16], [0,0], 'g' ) 30 | ax.plot3D( [36,36], [20.16,-20.16], [0,0], 'g' ) 31 | ax.plot3D( [52.5,36], [20.16,20.16], [0,0], 'g' ) 32 | ax.plot3D( [52.5,36], [-20.16,-20.16], [0,0], 'g' ) 33 | ax.plot3D( [-52.5,-36], [20.16,20.16], [0,0], 'g' ) 34 | ax.plot3D( [-52.5,-36], [-20.16,-20.16], [0,0], 'g' ) 35 | 36 | fig.suptitle( 'Kick_distribution' ) 37 | fig.savefig( date + '.png' ) 38 | -------------------------------------------------------------------------------- /extraction/referee.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | # goal [ cycle, team( l or r ) ] 5 | 6 | import cython 7 | 8 | from lib import la_class 9 | 10 | 11 | def countOurYellowCard( wm: la_class.WorldModel, team: str ) -> cython.int: 12 | 13 | our_yellow_card: cython.int = 0 14 | 15 | for say in wm.referee.say: 16 | if ( "(referee yellow_card_" in say ): 17 | tmp: list = say.split( "_" ) 18 | if ( tmp[2] == team ): 19 | our_yellow_card = 1 20 | 21 | return our_yellow_card 22 | 23 | 24 | def countOppYellowCard( wm: la_class.WorldModel, team: str ) -> cython.int: 25 | 26 | opp_yellow_card: cython.int = 0 27 | 28 | for say in wm.referee.say: 29 | if ( "(referee yellow_card_" in say ): 30 | tmp: list = say.split( "_" ) 31 | if ( tmp[2] != team ): 32 | opp_yellow_card = 1 33 | 34 | return opp_yellow_card 35 | 36 | 37 | def getCurrentPlayMode( line: str ) -> str: 38 | 39 | return line.split()[2].strip( ")" ) 40 | 41 | 42 | def setPlayMode( current_play_mode: str, wm: la_class.WorldModel ) -> None: 43 | 44 | wm.referee.playmode = current_play_mode 45 | 46 | 47 | def sayMessage( line: str, wm: la_class.WorldModel ) -> None: 48 | 49 | wm.referee.say.append( line ) 50 | -------------------------------------------------------------------------------- /calculation/kick_sequence.pxd: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #!/usr/bin/env python 3 | # cython: language_level=3 4 | 5 | import cython 6 | from lib cimport la_class 7 | 8 | @cython.locals(INVALID_VALUE=cython.double, START_SEQUENCE=cython.int, BETWEEN_SEQUENCE=cython.int, END_SEQUENCE=cython.int, 9 | POSITIVE_LABEL=cython.int, NEGATIVE_LABEL=cython.int, tmp_kick_path_x=cython.list, tmp_kick_path_y=cython.list, 10 | tmp_kick_sequence=cython.list) 11 | cdef void appendSequence( la_class.Feature feat, str color, double kick_dist_thr=? ) 12 | 13 | # cdef printSequence( la_class.ServerParam sp, la_class.Feature feat ): 14 | 15 | cdef void finishSequence( la_class.Feature feat, str color ) 16 | 17 | cdef void clearList( la_class.Feature feat ) 18 | 19 | @cython.locals(last_kick_side=cython.str, last_kick_cycle=cython.int, kick_side=cython.str, kick_cycle=cython.int, 20 | NO_KICK=cython.int, NO_KICKER=cython.int, NO_RECEIVER=cython.int, kicked_from=cython.int, kicked_to=cython.int, 21 | ) 22 | cdef int getSequence( la_class.WorldModel wm, la_class.ServerParam sp, int cycle, la_class.Feature feat, bint until_penalty_area=?) 23 | 24 | cdef void considerSameTimingKick( la_class.WorldModel wm, int cycle, int start_cycle, la_class.Feature feat) 25 | 26 | cdef void saveKickSequence( la_class.Feature feat, bint outputKickedCycle=?) -------------------------------------------------------------------------------- /calculation/through_pass.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | # -*- coding: utf-8 -* 4 | 5 | import cython 6 | 7 | from lib import la_class 8 | 9 | def isThroughPass( wm: list, cycle: cython.int ) -> cython.bint: 10 | last_kicked_cycle: cython.int = wm[cycle].last_kicked_cycle 11 | 12 | if ( wm[cycle+1].dominate_side == wm[cycle].dominate_side \ 13 | and wm[cycle+1].last_kicker_unum != wm[cycle].last_kicker_unum ): 14 | 15 | if ( wm[cycle+1].dominate_side == "l" \ 16 | and wm[cycle+1].ball.pos.x - wm[last_kicked_cycle].ball.pos.x > 5.0 \ 17 | and wm[cycle+1].ball.pos.x > 15.0 \ 18 | and wm[last_kicked_cycle].r.offsideLineX < wm[cycle+1].ball.pos.x ): 19 | 20 | return True 21 | 22 | if ( wm[cycle+1].dominate_side == "r" \ 23 | and wm[cycle+1].ball.pos.x - wm[last_kicked_cycle].ball.pos.x < -5.0 \ 24 | and wm[cycle+1].ball.pos.x < -15.0 \ 25 | and wm[last_kicked_cycle].l.offsideLineX > wm[cycle+1].ball.pos.x ): 26 | 27 | return True 28 | 29 | return False 30 | 31 | 32 | def countThroughPass( wm: list, cycle: cython.int, feat: la_class.Feature ) -> None: 33 | 34 | if ( isThroughPass( wm, cycle ) ): 35 | if ( not __debug__ ): 36 | print ( "this pass is through pass" ) 37 | if ( wm[cycle+1].dominate_side == feat.target_team ): 38 | feat.our_through_pass += 1 39 | else: 40 | feat.opp_through_pass += 1 41 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import glob 4 | import shutil 5 | 6 | from distutils.core import setup, Extension 7 | from Cython.Build import cythonize 8 | 9 | compile_list = [ 10 | #[library name, place of this library] 11 | ["la_class", "lib"], 12 | ["lib_log_analyzer", "lib"], 13 | ["option", "lib"], 14 | ["state", "lib"], 15 | ["filename_split", "extraction"], 16 | ["get_tackle", "extraction"], 17 | ["get_kick", "extraction"], 18 | ["get_dash", "extraction"], 19 | ["rcg_reader", "extraction"], 20 | ["rcl_reader", "extraction"], 21 | ["hetero", "extraction"], 22 | ["referee", "extraction"], 23 | ["extract", "extraction"], 24 | ["dribble_count", "calculation"], 25 | ["kick_distribution", "calculation"], 26 | ["kick_sequence", "calculation"], 27 | ["offside_line", "calculation"], 28 | ["pass_check", "calculation"], 29 | ["shoot", "calculation"], 30 | ["through_pass", "calculation"], 31 | ["calculate", "calculation"] 32 | ] 33 | 34 | # compile 35 | print("compile cython") 36 | ext = [] 37 | for l_name, place in compile_list: 38 | ext.append(Extension(l_name, [place+"/"+l_name+".py"], include_dirs=["."])) 39 | setup(name="loganalyzer3", ext_modules=cythonize(ext)) 40 | 41 | # mv libraries 42 | for l_name, place in compile_list: 43 | pathname = glob.glob("./"+l_name+".cpython-*-linux-gnu.so")[0] 44 | filename = os.path.split(pathname)[1] 45 | shutil.move(pathname, "./"+place+"/"+filename) 46 | 47 | print("finish compilation!") 48 | -------------------------------------------------------------------------------- /lib/option.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | 5 | import argparse 6 | 7 | def parser(): 8 | usage = 'python main.py [--side ] [--team ] [--help]' 9 | parser = argparse.ArgumentParser( usage=usage ) 10 | parser.add_argument( "filename" ) 11 | parser.add_argument( "-s", "--side", 12 | default=None, 13 | help="select target team side" ) 14 | parser.add_argument( "-t", "--team", 15 | default=None, 16 | help="select target team name") 17 | parser.add_argument( "-g", "--game", 18 | action="store_true", 19 | default=False, 20 | help="output csv file about the almost all game-information") 21 | parser.add_argument( "--each-cycle", 22 | action="store_true", 23 | default=False, 24 | help="output result for each cycle") 25 | parser.add_argument( "--start-cycle", 26 | type=int, 27 | default=0, 28 | help="start cycle") 29 | parser.add_argument( "--end-cycle", 30 | type=int, 31 | default=8000, 32 | help="end cycle") 33 | parser.add_argument( "-o", "--output-dir", 34 | type=str, 35 | default='./', 36 | help="path of the files to be output") 37 | 38 | #print (parser.parse_args()) 39 | 40 | return parser.parse_args() 41 | -------------------------------------------------------------------------------- /lib/state.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | 8 | 9 | def isGoalie( cycle: cython.int, unum: cython.int, side: str, wm: list ) -> cython.bint: 10 | 11 | if ( side == "l" ): 12 | if( int( wm[cycle].l.player[unum].state, 16 ) & int( 0x00000008 ) == 8 ): 13 | return True 14 | else: 15 | return False 16 | 17 | elif ( side == "r"): 18 | if( int( wm[cycle].r.player[unum].state, 16 ) & int( 0x00000008 ) == 8 ): 19 | return True 20 | else: 21 | return False 22 | 23 | def isDead( cycle: cython.int, unum: cython.int, side: str, wm: list ) -> cython.bint: 24 | 25 | if ( side == 'l' ): 26 | if ( wm[cycle].l.player[unum].state == '0' ): 27 | return True 28 | else: 29 | return False 30 | 31 | elif ( side == 'r' ): 32 | if ( wm[cycle].r.player[unum].state == '0' ): 33 | return True 34 | else: 35 | return False 36 | 37 | 38 | def checkTackle( state: str ) -> cython.bint: 39 | if ( int( state, 16 ) & int( 0x00002000 ) == 8192 ): 40 | return False 41 | else: 42 | return True 43 | 44 | 45 | def checkKick( wm: la_class.WorldModel, unum: cython.int, side: str ) -> cython.bint: 46 | 47 | if ( side == "l" ): 48 | if ( int( wm.l.player[unum].state, 16 ) & int( 0x00000004 ) == 4 ): 49 | return False 50 | 51 | else: 52 | return True 53 | 54 | elif ( side == "r" ): 55 | if ( int( wm.r.player[unum].state, 16 ) & int( 0x00000004 ) == 4 ): 56 | return False 57 | 58 | else: 59 | return True 60 | -------------------------------------------------------------------------------- /extraction/filename_split.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | # This file is module of filename 7 | # get an argument which is the name of target team 8 | # then devide filename into target team and opponent team 9 | # there are 4 return value [the name of target team , thename of opponent team , target team's score , opponent team's score ] 10 | 11 | 12 | def splitFileName(logname: str, l_teamname: str, r_teamname: str, team: str) -> list: 13 | left: cython.str = str(logname.split("-vs-")[0].split("-", 1)[1].rsplit("_", 1)[0]) 14 | right: cython.str = str(logname.split("-vs-")[1].rsplit("_", 1)[0]) 15 | l_score: cython.str = logname.split("-vs-")[0].rsplit("_", 1)[1] 16 | r_score: cython.str = logname.split("-vs-")[1].rsplit("_", 1)[1] 17 | l_ps_score: cython.str = "0" 18 | r_ps_score: cython.str = "0" 19 | 20 | if left != l_teamname and right != r_teamname: 21 | left = logname.split("-vs-")[0].split("-", 1)[1].rsplit("_", 2)[0] 22 | right = logname.split("-vs-")[1].rsplit("_", 2)[0] 23 | l_score = logname.split("-vs-")[0].rsplit("_", 2)[1] 24 | r_score = logname.split("-vs-")[1].rsplit("_", 2)[1] 25 | l_ps_score = logname.split("-vs-")[0].rsplit("_", 2)[2] 26 | r_ps_score = logname.split("-vs-")[1].rsplit("_", 2)[2] 27 | if left != l_teamname or right != r_teamname: 28 | raise SyntaxError("team names are not matched...") 29 | 30 | if team == "l": 31 | return [left, right, int(l_score), int(r_score), int(l_ps_score), int(r_ps_score)] 32 | 33 | elif team == "r": 34 | return [right, left, int(r_score), int(l_score), int(r_ps_score), int(l_ps_score)] 35 | 36 | else: 37 | print("emergency error : file name doesn't correspond to rcg file") 38 | return ["none", "none", 0, 0, 0, 0] 39 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import os 5 | import glob 6 | import cython 7 | import argparse 8 | import traceback 9 | 10 | from lib import option 11 | from lib import la_class 12 | 13 | from extraction import extract 14 | from calculation import calculate as calc 15 | 16 | 17 | def doAnalyze(filename: str, args:argparse.Namespace) -> None: 18 | print(filename) 19 | 20 | # ------ initialization ------ # 21 | 22 | wm: list = [] 23 | 24 | for _ in range( args.start_cycle, args.end_cycle+1 ): 25 | wm.append(la_class.WorldModel("left", "right")) 26 | 27 | sp: la_class.ServerParam = la_class.ServerParam() 28 | feature: la_class.Feature = la_class.Feature() 29 | 30 | # ------ extraction ------ # 31 | 32 | extract.extractRcg( filename, args, wm, sp, feature ) 33 | if feature.team_point[0] == 'none' and feature.team_point[1] == 'none': 34 | print("Neither is the target team name. skip...") 35 | return 36 | 37 | extract.extractRcl( filename, args, wm, sp ) 38 | 39 | # ------ calculation ------ # 40 | 41 | calc.analyzeLog( args, wm, sp, feature ) 42 | 43 | #pass_probability = pb.passProbability( ball, kick, situation, tackle, player_state_l, player_state_r, team ) 44 | 45 | 46 | if __name__ == "__main__": 47 | 48 | # ------ options ------ # 49 | args: argparse.Namespace = option.parser() 50 | os.makedirs(args.output_dir, exist_ok=True) 51 | 52 | # ------ for directory ------ # 53 | if os.path.isdir(args.filename): 54 | filepath: str = args.filename if args.filename[-1] == "/" else '{}/'.format(args.filename) 55 | for f in sorted(glob.glob(filepath+'*.rcg*')): 56 | try: 57 | doAnalyze(f, args) 58 | except: 59 | traceback.print_exc() 60 | # ------ for single file ------ # 61 | else: 62 | doAnalyze(args.filename, args) 63 | -------------------------------------------------------------------------------- /extraction/rcl_reader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | 8 | 9 | def getInitialPosition( line: str, wm: la_class.WorldModel ) -> None: 10 | 11 | if ( line.split()[0].split(",")[0] != "0" ): 12 | return 13 | 14 | if ( "(move" in line ): 15 | move_teamname: cython.str = line.split()[2].rsplit( "_", 1 )[0] 16 | # move_cycle: cython.int = int( line.split()[0].split( "," )[0] ) 17 | move_player: cython.int = int( line.split()[2].rsplit( "_", 1 )[1].strip( ":" ) ) 18 | 19 | for i in range( len( line.split() ) ): 20 | if ( "move" in line.split()[i] ): 21 | seq = i 22 | 23 | if ( wm.l.name == move_teamname ): 24 | wm.l.player[ move_player - 1 ].pos.x = float( line.split()[seq+1] ) 25 | try: 26 | wm.l.player[ move_player - 1 ].pos.y = float( line.split()[seq+2].split(")(")[0] ) 27 | except ValueError: 28 | wm.l.player[ move_player - 1 ].pos.y = float( line.split()[seq+2].replace(")","") ) 29 | elif ( wm.r.name == move_teamname ): 30 | wm.r.player[ move_player - 1 ].pos.x = float( line.split()[seq+1] ) 31 | try: 32 | wm.r.player[ move_player - 1 ].pos.y = float( line.split()[seq+2].split(")(")[0] ) 33 | except ValueError: 34 | wm.r.player[ move_player - 1 ].pos.y = float( line.split()[seq+2].replace(")","") ) 35 | 36 | 37 | def getAction( line: str, wm: la_class.WorldModel ) -> None: 38 | 39 | teamname: cython.str = line.split()[2].rsplit( "_", 1 )[0] 40 | unum: cython.int = int( line.split()[2].rsplit( "_", 1 )[1].strip( ":" ) ) 41 | 42 | if ( teamname == wm.l.name ): 43 | wm.l.player[unum - 1].action = line.split(": ")[1].strip( "\n" ) 44 | elif ( teamname == wm.r.name ): 45 | wm.r.player[unum - 1].action = line.split(": ")[1].strip( "\n" ) 46 | -------------------------------------------------------------------------------- /loop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FILE=$1 4 | DIR=`dirname $0` 5 | 6 | TMP_FILENAME="rcglist.txt" 7 | 8 | startcycle=0 9 | endcycle=6000 10 | opt="" 11 | 12 | usage() 13 | { 14 | (echo "Usage: $0 dirname [options]" 15 | echo "Available options:" 16 | echo " --help prints this" 17 | echo " -l|--side l analyzes left team" 18 | echo " -r|--side r analyzes right team" 19 | echo " --team TEAMNAME analyzes TEAMNAME team" 20 | echo " --each-cycle outputs results for each cycle" 21 | echo " --start-cycle sets start cycle" 22 | echo " --end-cycle sets end cycle" 23 | ) 24 | } 25 | 26 | 27 | if [ ! -d $1 ]; then 28 | usage 29 | exit 1 30 | fi 31 | 32 | while [ $# -gt 0 ] 33 | do 34 | case $2 in 35 | 36 | --help) 37 | usage 38 | exit 0 39 | ;; 40 | 41 | -l) 42 | opt="${opt} -s l" 43 | ;; 44 | 45 | -r) 46 | opt="${opt} -s r" 47 | ;; 48 | 49 | --side) 50 | if [ $# -lt 3 ]; then 51 | usage 52 | exit 1 53 | fi 54 | opt="${opt} -s ${3}" 55 | shift 1 56 | ;; 57 | 58 | --team) 59 | if [ $# -lt 3 ]; then 60 | usage 61 | exit 1 62 | fi 63 | opt="${opt} -t ${3}" 64 | shift 1 65 | ;; 66 | 67 | --each-cycle) 68 | opt="${opt} --each-cycle" 69 | ;; 70 | 71 | --start-cycle) 72 | if [ $# -lt 3 ]; then 73 | usage 74 | exit 1 75 | fi 76 | opt="${opt} --start-cycle ${3}" 77 | shift 1 78 | ;; 79 | 80 | --end-cycle) 81 | if [ $# -lt 3 ]; then 82 | usage 83 | exit 1 84 | fi 85 | opt="${opt} --end-cycle ${3}" 86 | shift 1 87 | ;; 88 | 89 | esac 90 | 91 | shift 1 92 | done 93 | 94 | ls ${FILE}*.rcg > /dev/null 2>&1 95 | if [ $? -eq 0 ]; then 96 | ls ${FILE}*.rcg > ${TMP_FILENAME} 97 | fi 98 | ls ${FILE}*.rcg.gz > /dev/null 2>&1 99 | if [ $? -eq 0 ]; then 100 | ls ${FILE}*.rcg.gz >> ${TMP_FILENAME} 101 | fi 102 | 103 | count=0 104 | 105 | cat ${TMP_FILENAME} | while read log; do 106 | echo ${log} 107 | if [ $count -eq 0 ]; then 108 | python ${DIR}/main.py ${log} ${opt} 109 | else 110 | python ${DIR}/main.py ${log} ${opt} "--without-index" 111 | fi 112 | count=$(($count+1)) 113 | done 114 | 115 | rm ${TMP_FILENAME} 116 | -------------------------------------------------------------------------------- /extraction/get_kick.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from extraction import get_tackle as gt 7 | from lib import la_class 8 | 9 | def checkKick( wm: la_class.WorldModel, unum: cython.int, side: str ) -> cython.bint: 10 | 11 | if ( side == "l" ): 12 | if ( int( wm.l.player[unum].state, 16 ) & int( 0x00000004 ) == 4 ): 13 | return False 14 | 15 | else: 16 | return True 17 | 18 | elif ( side == "r" ): 19 | if ( int( wm.r.player[unum].state, 16 ) & int( 0x00000004 ) == 4 ): 20 | return False 21 | 22 | else: 23 | return True 24 | 25 | 26 | def isKick( wm: list, cycle: cython.int ) -> cython.int: 27 | 28 | num_kick: cython.int = 0 29 | kick_side_l: cython.bint = False 30 | kick_side_r: cython.bint = False 31 | 32 | for unum in range( 11 ): 33 | 34 | if ( ( "(kick" in wm[cycle].l.player[unum].action \ 35 | or "(tackle" in wm[cycle].l.player[unum].action ) \ 36 | and ( checkKick( wm[cycle+1], unum, "l" ) \ 37 | and gt.checkTackle( wm[cycle+1].l.player[unum].state ) ) ): 38 | if ( not wm[cycle].referee.said ): 39 | num_kick += 1 40 | wm[ cycle + 1 ].last_kicker_unum = unum 41 | wm[ cycle + 1 ].last_kicked_cycle = cycle + 1 42 | kick_side_l = True 43 | else: 44 | num_kick += 1 45 | 46 | 47 | if ( ( "(kick" in wm[cycle].r.player[unum].action \ 48 | or "(tackle" in wm[cycle].r.player[unum].action ) \ 49 | and ( checkKick( wm[cycle+1], unum, "r" ) \ 50 | and gt.checkTackle( wm[cycle+1].r.player[unum].state ) ) ): 51 | if ( not wm[cycle].referee.said ): 52 | num_kick += 1 53 | wm[ cycle + 1 ].last_kicker_unum = unum 54 | wm[ cycle + 1 ].last_kicked_cycle = cycle + 1 55 | kick_side_r = True 56 | else: 57 | num_kick += 1 58 | 59 | 60 | if ( kick_side_l and kick_side_r ): 61 | wm[ cycle + 1 ].dominate_side = "neutral" 62 | wm[ cycle + 1 ].last_kicker_unum = -1 63 | return num_kick 64 | 65 | elif ( kick_side_l ): 66 | wm[ cycle + 1 ].dominate_side = "l" 67 | return num_kick 68 | 69 | elif ( kick_side_r ): 70 | wm[ cycle + 1 ].dominate_side = "r" 71 | return num_kick 72 | 73 | wm[ cycle + 1 ].dominate_side = wm[ cycle ].dominate_side 74 | wm[ cycle + 1 ].last_kicker_unum = wm[ cycle ].last_kicker_unum 75 | wm[ cycle + 1 ].last_kicked_cycle = wm[ cycle ].last_kicked_cycle 76 | return num_kick 77 | -------------------------------------------------------------------------------- /extraction/get_tackle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | 8 | def checkTackle( state: str ) -> cython.bint: 9 | if ( int( state, 16 ) & int( 0x00002000 ) == 8192 ): 10 | return False 11 | else: 12 | return True 13 | 14 | 15 | def countOurTackle( wm: la_class.WorldModel, team: str ) -> cython.int: 16 | all_tackle: cython.int = 0 17 | 18 | if ( team == "l" ): 19 | for unum in range( 11 ): 20 | if ( "(tackle" in wm.l.player[unum].action ): 21 | all_tackle += 1 22 | 23 | elif ( team == "r" ): 24 | for unum in range( 11 ): 25 | if ( "(tackle" in wm.r.player[unum].action ): 26 | all_tackle += 1 27 | 28 | return all_tackle 29 | 30 | 31 | def countOppTackle( wm: la_class.WorldModel, team: str ) -> cython.int: 32 | all_tackle: cython.int = 0 33 | 34 | if ( team == "l" ): 35 | for unum in range( 11 ): 36 | if ( "(tackle" in wm.r.player[unum].action ): 37 | all_tackle += 1 38 | 39 | elif ( team == "r" ): 40 | for unum in range( 11 ): 41 | if ( "(tackle" in wm.l.player[unum].action ): 42 | all_tackle += 1 43 | 44 | return all_tackle 45 | 46 | 47 | def countOurSuccessTackle( wm: la_class.WorldModel, next_wm: la_class.WorldModel, team: str ) -> cython.int: 48 | success_tackle: cython.int = 0 49 | 50 | if ( team == "l" ): 51 | for unum in range( 11 ): 52 | if ( "(tackle" in wm.l.player[unum].action \ 53 | and checkTackle( next_wm.l.player[unum].state ) ): 54 | success_tackle += 1 55 | 56 | if ( team == "r" ): 57 | for unum in range( 11 ): 58 | if ( "(tackle" in wm.r.player[unum].action \ 59 | and checkTackle( next_wm.r.player[unum].state ) ): 60 | success_tackle += 1 61 | 62 | return success_tackle 63 | 64 | 65 | def countOppSuccessTackle( wm: la_class.WorldModel, next_wm: la_class.WorldModel, team: str ) -> cython.int: 66 | success_tackle: cython.int = 0 67 | 68 | if ( team == "l" ): 69 | for unum in range( 11 ): 70 | if ( "(tackle" in wm.r.player[unum].action \ 71 | and checkTackle( next_wm.r.player[unum].state ) ): 72 | success_tackle += 1 73 | 74 | if ( team == "r" ): 75 | for unum in range( 11 ): 76 | if ( "(tackle" in wm.l.player[unum].action \ 77 | and checkTackle( next_wm.l.player[unum].state ) ): 78 | success_tackle += 1 79 | 80 | return success_tackle 81 | -------------------------------------------------------------------------------- /calculation/shoot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | # -*- coding: utf-8 -*- 4 | 5 | # implemented by Hori 6 | 7 | import cython 8 | 9 | from lib import la_class 10 | 11 | def isOurShoot( wm: la_class.WorldModel, sp: la_class.ServerParam, cycle: cython.int, side: str ) -> cython.bint: 12 | 13 | x: cython.double = wm.ball.pos.x 14 | y: cython.double = wm.ball.pos.y 15 | xv: cython.double = wm.ball.vel.x 16 | yv: cython.double = wm.ball.vel.y 17 | 18 | if ( side == 'l' ): 19 | while x < sp.goal_line_r and ( xv ** 2 + yv ** 2 ) > 1: 20 | x = x + xv 21 | y = y + yv 22 | xv = xv * sp.ball_speed_decay 23 | yv = yv * sp.ball_speed_decay 24 | if x >= sp.goal_line_r and y > - ( sp.goal_post ) and y < sp.goal_post: 25 | if ( not __debug__ ): 26 | print ( "our", wm.last_kicker_unum+1, "shoot!:", cycle + 1 ) 27 | return True 28 | 29 | elif( side == 'r' ): 30 | while x > sp.goal_line_l and ( xv ** 2 + yv ** 2 ) > 1: 31 | x = x + xv 32 | y = y + yv 33 | xv = xv * sp.ball_speed_decay 34 | yv = yv * sp.ball_speed_decay 35 | if x <= sp.goal_line_l and y > - ( sp.goal_post ) and y < sp.goal_post: 36 | if ( not __debug__ ): 37 | print ( "our", wm.last_kicker_unum+1, "shoot!:", cycle + 1 ) 38 | return True 39 | return False 40 | 41 | 42 | def isOppShoot( wm: la_class.WorldModel, sp: la_class.ServerParam, cycle: cython.int, side: str ) -> cython.bint: 43 | 44 | x: cython.double = wm.ball.pos.x 45 | y: cython.double = wm.ball.pos.y 46 | xv: cython.double = wm.ball.vel.x 47 | yv: cython.double = wm.ball.vel.y 48 | 49 | if ( side == 'l' ): 50 | opp_side: cython.str = 'r' 51 | elif ( side == 'r' ): 52 | opp_side: cython.str = 'l' 53 | 54 | if ( opp_side == 'l' ): 55 | while x < sp.goal_line_r and ( xv ** 2 + yv ** 2 ) > 1: 56 | x = x + xv 57 | y = y + yv 58 | xv = xv * sp.ball_speed_decay 59 | yv = yv * sp.ball_speed_decay 60 | if x >= sp.goal_line_r and y > - ( sp.goal_post ) and y < sp.goal_post: 61 | if ( not __debug__ ): 62 | print ( "opp", wm.last_kicker_unum+1, "shoot!:", cycle + 1 ) 63 | return True 64 | 65 | elif( opp_side == 'r' ): 66 | while x > sp.goal_line_l and ( xv ** 2 + yv ** 2 ) > 1: 67 | x = x + xv 68 | y = y + yv 69 | xv = xv * sp.ball_speed_decay 70 | yv = yv * sp.ball_speed_decay 71 | if x <= sp.goal_line_l and y > - ( sp.goal_post ) and y < sp.goal_post: 72 | if ( not __debug__ ): 73 | print ( "opp", wm.last_kicker_unum+1, "shoot!:", cycle + 1 ) 74 | return True 75 | return False 76 | -------------------------------------------------------------------------------- /extraction/kick_txt.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | 4 | from lib import lib_log_analyzer as lib 5 | 6 | def KickTxt( data, date ): 7 | 8 | if( len(data) == 3 ): 9 | a = data[1].split( ".r" )[0] 10 | team = data[2].split( "-" )[1] 11 | 12 | x = []; 13 | y = []; 14 | x_tmp = []; 15 | y_tmp = []; 16 | x_kick = []; 17 | y_kick = []; 18 | x_pass = []; 19 | y_pass = []; 20 | time = []; 21 | player = []; 22 | kick_player = []; 23 | pass_player = []; 24 | lines = []; 25 | 26 | left = a.split( "-vs-" )[0].split( "-", 1 )[1].rsplit( "_", 1 )[0] 27 | right = a.split( "-vs-" )[1].rsplit( "_", 1 )[0] 28 | cycle = '0' 29 | 30 | for l in open( a + ".rcg", 'r' ): 31 | 32 | if ( "show" in l ): 33 | 34 | tmp = l.split(); 35 | 36 | if( cycle != tmp[1] ): 37 | 38 | x.append( float( tmp[3] ) ) 39 | y.append( float( tmp[4] ) ) 40 | 41 | if( len( x ) == 2999 ): 42 | 43 | x.append( float( tmp[3] ) ) 44 | y.append( float( tmp[4] ) ) 45 | lines.append( l ) 46 | 47 | cycle = tmp[1] 48 | lines.append( l ) 49 | 50 | 51 | for m in open( a + ".rcl", 'r' ): 52 | 53 | if( "(kick" in m ): 54 | 55 | tmp = m.split(); 56 | logcycle = tmp[0].split( ',' ) 57 | time.append( int( logcycle[0] ) ) 58 | logteam = tmp[2].split( '_' ) 59 | 60 | while( len( logteam ) > 2 ): 61 | 62 | logteam[0] = logteam[0] + "_" + logteam[1] 63 | logteam.pop( 1 ) 64 | 65 | if( logteam[0] == left ): 66 | 67 | logteam[0] = 'l' 68 | 69 | else: 70 | 71 | logteam[0] = 'r' 72 | 73 | if( logteam[1] == '10:' or logteam[1] == '11:' ): 74 | 75 | logteam[1] = logteam[1][0] + logteam[1][1] 76 | 77 | else: 78 | 79 | logteam[1] = logteam[1][0] 80 | 81 | player.append( logteam ) 82 | 83 | 84 | 85 | for i in range( len( time ) ): 86 | 87 | tmp = lines[time[i]-1].split() 88 | 89 | for j in range( len( tmp )-1 ): 90 | 91 | if( tmp[j] == '(('+player[i][0] and tmp[j+1] == player[i][1]+')' ): 92 | 93 | x_tmp.append( float( tmp[j+4] ) ) 94 | y_tmp.append( float( tmp[j+5] ) ) 95 | 96 | if( math.sqrt( ( x_tmp[i] - x[time[i]-1] ) ** 2 + ( y_tmp[i] - y[time[i]-1] ) ** 2 ) < 1.5 ): 97 | 98 | x_kick.append( x[time[i]-1] ) 99 | y_kick.append( y[time[i]-1] ) 100 | kick_player.append( player[i] ) 101 | 102 | 103 | f = open( date + '_kick_dist.txt', 'a' ) 104 | 105 | 106 | for i in range( len( x_kick )-1 ): 107 | 108 | if( kick_player[i][0] == kick_player[i+1][0] and kick_player[i][0] == team ): 109 | 110 | dist = math.sqrt( ( x_kick[i] - x_kick[i+1] ) ** 2 + ( y_kick[i] - y_kick[i+1] ) ** 2 ) 111 | 112 | if( kick_player[i][0] == "l" ): 113 | 114 | f.write( str( x_kick[i] )+" "+str( y_kick[i] )+" "+str( dist )+'\n' ) 115 | 116 | elif( kick_player[i][0] == "r" ): 117 | 118 | f.write( str( -x_kick[i] )+" "+str( -y_kick[i] )+" "+str( dist )+'\n' ) 119 | 120 | f.close() 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LogAnalyzer3 2 | 3 | Log analysis tool for RoboCup Soccer Simulation 2D 4 | 5 | ## Data obtained 6 | 7 | - Date 8 | - Our team name 9 | - Opp team name 10 | - Our final team point 11 | - Opp final team point 12 | - Our penalty shootout point 13 | - Opp penalty shootout point 14 | - Final result (win = 3, lose = 0, draw = 1) 15 | - Our domination time 16 | - Opp domination time 17 | - Our possession 18 | - Opp possession 19 | - Number our yellow cards 20 | - Number opp yellow cards 21 | - Number of our passes 22 | - Number of our passes (only left direction) 23 | - Number of our passes (only right direction) 24 | - Number of our passes (only front direction) 25 | - Number of our passse (only back direction) 26 | - Number of opp passes 27 | - Number of opp passes (only left direction) 28 | - Number of opp passes (only right direction) 29 | - Number of opp passes (only front direction) 30 | - Number of opp passes (only back direction) 31 | - Number of our through passes 32 | - Number of opp through passes 33 | - Number of our successed tackles 34 | - Number of our failed tackles 35 | - Number of opp successed tackles 36 | - Number of opp failed tackles 37 | - Number of our shoots 38 | - Number of opp shoots 39 | - Our point 40 | - Opp point 41 | - Number of our dribbles 42 | - Number of opp dribbles 43 | - Number of entering into ball opp penalty area 44 | - Number of entering into ball our penalty area (currently not worked, always returns -1) 45 | - Number of our disconnected players 46 | - Number of opp disconnected players 47 | 48 | ## How to Use 49 | 50 | ### Add path 51 | 52 | if you add `export PATH="/path/to/loganalyzer3:$PATH"` at the bottom of the "~/.bashrc", you can use loganalyzer3 everywhere 53 | 54 | ### Required libraries 55 | 56 | - matplotlib 57 | - If you want to plot "Kick distribution" or "Kick sequence", latex environment is required. 58 | - `sudo apt install texlive texlive-latex-extra texlive-fonts-recommended dvipng msttcorefonts` 59 | - cython (if you compile it) 60 | 61 | you can get all required libraries by the following command. 62 | 63 | `pip install -r requirements.txt` 64 | 65 | ### Compile cython 66 | 67 | you can compile cython files (work for only Ubuntu OS) 68 | 69 | `/path/to/loganalyzer3/build.sh` 70 | 71 | you can also use this analyzer without compilation 72 | 73 | ### Clean cython files 74 | 75 | `/path/to/loganalyzer3/clean.sh` 76 | 77 | ### Options 78 | 79 | #### Examples of options 80 | 81 | ##### debug mode: 82 | 83 | `--debug` 84 | 85 | output to stdout when and who do pass, and output figures of action_sequences and kick_distributions 86 | 87 | ##### each cycle mode: 88 | 89 | `--each-cycle` 90 | 91 | output all intermediate results for each cycle 92 | 93 | ### Examples of Execution 94 | 95 | - `loganalyzer3 /path/to/dir --side l --output-dir /path/to/savedir/` 96 | - `loganalyzer3 /path/to/file --side l --debug` 97 | - `loganalyzer3 /path/to/dir --team HELIOS_base --each-cycle` 98 | 99 | ### Note 100 | 101 | - Data are outputted in csv format 102 | - File name is determined by the target teams (e.g. HELIOS_base.csv) 103 | - Save directory is same as the directory at which you execute `loganalyzer3` 104 | -------------------------------------------------------------------------------- /lib/lib_log_analyzer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import math 5 | import cython 6 | import argparse 7 | 8 | from lib import la_class 9 | 10 | # index c 11 | 12 | def calcDist(pos1: la_class.Position, pos2: la_class.Position) -> cython.double: 13 | 14 | return math.sqrt(math.pow((pos1.x - pos2.x), 2) + math.pow((pos1.y - pos2.y), 2)) 15 | 16 | 17 | def calcDistC(x1: cython.double, y1: cython.double, x2: cython.double, y2: cython.double) -> cython.double: 18 | 19 | return math.sqrt(math.pow((x1 - x2), 2) + math.pow((y1 - y2), 2)) 20 | 21 | 22 | def calcRadian(pos1: la_class.Position, pos2: la_class.Position) -> cython.double: 23 | 24 | return math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) 25 | 26 | 27 | def calcRadianC(x1: cython.double, y1: cython.double, x2: cython.double, y2: cython.double) -> cython.double: 28 | 29 | return math.atan2(y2 - y1, x2 - x1) 30 | 31 | 32 | def changeRadianToDegree(radian: cython.double) -> cython.double: 33 | 34 | return radian * 180 / math.pi 35 | 36 | 37 | def countPlayOn(cycle1: cython.int, cycle2: cython.int, situation: list) -> cython.int: 38 | 39 | cnt: cython.int = 0 40 | 41 | for i in range(cycle1, cycle2): 42 | if isPlayOn(i, situation): 43 | cnt += 1 44 | 45 | return cnt 46 | 47 | 48 | # index g 49 | 50 | def getFileName(data: str) -> str: 51 | 52 | return data.split(".r")[0] 53 | 54 | 55 | def getResult(feat: la_class.Feature) -> cython.int: 56 | # compare the results of normal (and extra) halves 57 | if feat.team_point[2] > feat.team_point[3]: 58 | return 3 59 | elif feat.team_point[2] < feat.team_point[3]: 60 | return 0 61 | else: 62 | # compare the results of penalty shootouts 63 | if feat.team_point[4] > feat.team_point[5]: 64 | return 3 65 | elif feat.team_point[4] < feat.team_point[5]: 66 | return 0 67 | # draw game 68 | return 1 69 | 70 | 71 | # index i 72 | 73 | def isPlayOn(cycle: cython.int, situation: list) -> cython.bint: 74 | 75 | for i in range(len(situation) - 1): 76 | 77 | if situation[i][0] < cycle < situation[i+1][0]: 78 | 79 | if situation[i][1] == "t": 80 | return True 81 | 82 | elif situation[i][0] > cycle: 83 | break 84 | 85 | return False 86 | 87 | 88 | def isSameCycle(now_count: cython.int, pre_count: cython.int) -> cython.bint: 89 | 90 | if now_count == pre_count: 91 | return True 92 | else: 93 | return False 94 | 95 | 96 | # index s 97 | 98 | def selectTargetTeam(args: argparse.Namespace, l_teamname: str, r_teamname: str) -> str: 99 | if args.side == "l": 100 | return "l" 101 | 102 | elif args.side == "r": 103 | return "r" 104 | 105 | elif args.team == l_teamname: 106 | return "l" 107 | 108 | elif args.team == r_teamname: 109 | return "r" 110 | 111 | else: 112 | return "unknown" 113 | 114 | 115 | def sortPlayerUnumFromPos(player_list: list, target_pos: la_class.Position) -> list: 116 | return sorted(player_list, key=lambda player: calcDist(player.pos, target_pos)) 117 | -------------------------------------------------------------------------------- /lib/la_class.pxd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | cdef class Feature: 5 | cdef str logname 6 | cdef str target_team 7 | cdef list index 8 | cdef list kick_cycle 9 | cdef list kick_path_x 10 | cdef list kick_path_y 11 | cdef list all_kick_path_x 12 | cdef list all_kick_path_y 13 | cdef list color4plt_ks 14 | cdef list teammate_from_ball 15 | cdef list opponent_from_ball 16 | cdef list kicker 17 | cdef list receiver 18 | cdef list kick_sequence 19 | cdef list team_point 20 | cdef str date 21 | cdef int final_result 22 | cdef int our_yellow 23 | cdef int opp_yellow 24 | cdef int our_tackle 25 | cdef int all_our_tackle 26 | cdef int opp_tackle 27 | cdef int all_opp_tackle 28 | cdef int our_shoot 29 | cdef int opp_shoot 30 | cdef int our_dominate_time 31 | cdef int opp_dominate_time 32 | cdef double our_possession 33 | cdef double opp_possession 34 | cdef list our_kick 35 | cdef list opp_kick 36 | cdef list our_pass 37 | cdef list opp_pass 38 | cdef list pass_propability 39 | cdef int our_through_pass 40 | cdef int opp_through_pass 41 | cdef int penalty_area 42 | cdef int our_point 43 | cdef int opp_point 44 | cdef int our_dribble 45 | cdef int opp_dribble 46 | # cdef int our_dribble_dist 47 | # cdef int opp_dribble_dist 48 | cdef int our_penalty_area 49 | cdef int opp_penalty_area 50 | cdef int our_disconnected_player 51 | cdef int opp_disconnected_player 52 | 53 | 54 | # cdef __init__(self) 55 | cdef void outputIndexForIR(self, int start, int end) 56 | cdef void outputIndexForR(self, str filename, str side) 57 | cdef void outputIntegrateResult(self, int start, int end) 58 | cdef void outputResult(self, str filename, str side) 59 | 60 | 61 | cdef class Team: 62 | cdef str name 63 | cdef list player 64 | cdef double offsideLineX 65 | 66 | # cdef __init__(self, str teamname) 67 | 68 | 69 | cdef class Player: 70 | cdef int hetero 71 | cdef str state 72 | cdef Position pos 73 | cdef Position vel 74 | cdef double body_angle 75 | cdef double neck_angle 76 | cdef int view_area 77 | cdef int stamina 78 | cdef int stamina_capacity 79 | cdef str action 80 | 81 | # cdef __init__(self) 82 | 83 | 84 | cdef class Position: 85 | cdef double x 86 | cdef double y 87 | 88 | # cdef __init__(self) 89 | 90 | 91 | cdef class Ball: 92 | cdef Position pos 93 | cdef Position vel 94 | 95 | # cdef __init__(self) 96 | 97 | 98 | cdef class Referee: 99 | cdef str playmode 100 | cdef list say 101 | cdef bint said 102 | 103 | # cdef __init__(self) 104 | 105 | 106 | cdef class ServerParam: 107 | cdef double pitch_length 108 | cdef double pitch_width 109 | cdef double goal_line_l 110 | cdef double goal_line_r 111 | cdef int goal_post 112 | cdef double penalty_area_x 113 | cdef double penalty_area_y 114 | cdef double ball_speed_decay 115 | cdef list hetero 116 | 117 | # cdef __init__(self) 118 | 119 | 120 | cdef class HeteroParam: 121 | cdef double player_speed_max 122 | cdef double stamina_inc_max 123 | cdef double player_decay 124 | cdef double inertia_moment 125 | cdef double dash_power_rate 126 | cdef double player_size 127 | cdef double kickable_margin 128 | cdef double kick_land 129 | cdef double extra_stamina 130 | cdef double effort_max 131 | cdef double effort_min 132 | cdef double kick_power_rate 133 | cdef double foul_detect_probability 134 | cdef double catchable_area_l_stretch 135 | 136 | # cdef __init__(self) 137 | 138 | 139 | cdef class WorldModel: 140 | cdef Ball ball 141 | cdef Team l 142 | cdef Team r 143 | cdef Referee referee 144 | cdef str dominate_side 145 | cdef int last_kicker_unum 146 | cdef int last_kicked_cycle 147 | 148 | # cdef __init__(self, str team_l, str team_r) 149 | 150 | -------------------------------------------------------------------------------- /extraction/extract.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import os 5 | import gzip 6 | import cython 7 | import argparse 8 | 9 | from . import rcg_reader as rcg 10 | from . import rcl_reader as rcl 11 | from . import hetero 12 | from . import referee 13 | from . import filename_split as fns 14 | 15 | from lib import lib_log_analyzer as lib 16 | from lib import la_class 17 | 18 | 19 | def extractRcg(raw_filename: str, args: argparse.Namespace, wm: list, sp: la_class.ServerParam, feature: la_class.Feature) -> None: 20 | cycle: int = 0 21 | current_play_mode: cython.str = "unknown" 22 | 23 | filename: cython.str = lib.getFileName(raw_filename) 24 | if os.path.isfile(filename + ".rcg.gz"): 25 | filename += ".rcg.gz" 26 | elif os.path.isfile(filename + ".rcg"): 27 | filename += ".rcg" 28 | else: 29 | raise FileNotFoundError(filename + ".rcg.gz or " + filename + ".rcg") 30 | 31 | try: 32 | f = gzip.open(filename, "rt") 33 | # try seek 34 | f.seek(1) 35 | f.seek(0) 36 | except OSError: 37 | f = open(filename, "r") 38 | 39 | for line in f: 40 | if "(team 1 " in line: 41 | l_teamname: cython.str 42 | r_teamname: cython.str 43 | l_teamname, r_teamname = line.split()[2:4] 44 | feature.target_team = lib.selectTargetTeam(args, l_teamname, r_teamname) 45 | 46 | if "(player_type (id" in line: 47 | hetero_id: cython.int = int(line.split()[2].strip(")(player_speed_max")) 48 | hetero.getHetero(line, hetero_id, sp) 49 | 50 | if "(playmode" in line: 51 | current_play_mode: cython.str = referee.getCurrentPlayMode(line) 52 | 53 | if "(show" in line: 54 | tmp_line: list = line.split() 55 | 56 | # consider analyze cycles 57 | if args.start_cycle > int(tmp_line[1]): 58 | continue 59 | elif args.end_cycle < int(tmp_line[1]): 60 | continue 61 | 62 | if not lib.isSameCycle(int(tmp_line[1]), cycle): 63 | cycle = int(tmp_line[1]) 64 | 65 | rcg.getInformation(tmp_line, wm[cycle - args.start_cycle]) 66 | referee.setPlayMode(current_play_mode, wm[cycle - args.start_cycle]) 67 | 68 | if cycle == 2999: 69 | cycle += 1 70 | rcg.getInformation(tmp_line, wm[cycle - args.start_cycle]) 71 | wm[cycle - args.start_cycle].referee.playmode = "time_over" 72 | 73 | if "(msg" in line and "(result" in line: 74 | tmp_result: list = line.split() 75 | feature.date = tmp_result[4] 76 | feature.logname = tmp_result[4] + "-" + tmp_result[5].strip(")\"") 77 | feature.team_point = fns.splitFileName(feature.logname, l_teamname, r_teamname, feature.target_team) 78 | feature.final_result = lib.getResult(feature) 79 | 80 | # file close 81 | f.close() 82 | 83 | # team name update 84 | for i in wm: 85 | i.l.name = l_teamname 86 | i.r.name = r_teamname 87 | 88 | 89 | def extractRcl(raw_filename, args, wm, sp): 90 | filename: cython.str = lib.getFileName(raw_filename) 91 | if os.path.isfile(filename + ".rcl.gz"): 92 | filename += ".rcl.gz" 93 | elif os.path.isfile(filename + ".rcl"): 94 | filename += ".rcl" 95 | else: 96 | raise FileNotFoundError(filename + ".rcl.gz or " + filename + ".rcl") 97 | 98 | try: 99 | f: io.TextIOWrapper = gzip.open(filename, "rt") 100 | # try seek 101 | f.seek(1) 102 | f.seek(0) 103 | except OSError: 104 | f: io.TextIOWrapper = open(filename, "r") 105 | 106 | for line in f: 107 | # consider analyze cycles 108 | if args.start_cycle > int(line.split()[0].split(",")[0]): 109 | continue 110 | elif args.end_cycle < int(line.split()[0].split(",")[0]): 111 | continue 112 | 113 | # initial position 114 | if args.start_cycle == 0: 115 | rcl.getInitialPosition(line, wm[0]) 116 | 117 | # getActions( Only playon ) 118 | if lib.isSameCycle(int(line.split()[0].split(",")[1]), 0): 119 | 120 | # ignore coach and referee 121 | if (not "_Coach" in line \ 122 | and not "(referee" in line): 123 | cycle: cython.int = int(line.split()[0].split(",")[0]) 124 | rcl.getAction(line, wm[cycle - args.start_cycle]) 125 | 126 | if "(referee" in line: 127 | cycle: cython.int = int(line.split()[0].split(",")[0]) 128 | referee.sayMessage(line, wm[cycle - args.start_cycle]) 129 | 130 | # file close 131 | f.close() 132 | -------------------------------------------------------------------------------- /calculation/pass_probability.py: -------------------------------------------------------------------------------- 1 | import math 2 | from extraction import get_tackle as gt 3 | 4 | 5 | def isLongKick( ball, kick, index ): 6 | 7 | start_cycle = kick[index][1] 8 | end_cycle = kick[index + 1][1] 9 | 10 | dist = math.sqrt ( pow( ball[start_cycle - 1][0] - ball[end_cycle - 1][0], 2.0 ) + pow( ball[start_cycle - 1][1] - ball[end_cycle - 1][1], 2.0 ) ) 11 | 12 | if kick[index][2] == kick[index + 1][2] and kick[index][0] == kick[index + 1][0]: 13 | return False 14 | 15 | if dist < 2.0: 16 | return False 17 | 18 | return True 19 | 20 | def passChecker( kick, index, team ): 21 | 22 | if kick[index][0] == kick[index + 1][0] and kick[index][2] != kick[index + 1][2] and kick[index][0] == team: 23 | return True 24 | 25 | return False 26 | 27 | 28 | def exceptionPass( ball, kick, situation, tackle, player_state_l, player_state_r, index, team ): 29 | 30 | if index == len( kick ) - 1: 31 | return False 32 | 33 | start_cycle = kick[index][1] 34 | end_cycle = kick[index + 1][1] 35 | 36 | if kick[index][0] != team: 37 | return False 38 | 39 | if not isLongKick( ball, kick, index ): 40 | return False 41 | 42 | # ----- check pass is consecutive or not ----- # 43 | 44 | for i in range( len( situation ) ): 45 | if situation[i][1] == "f" and situation[i][0] > start_cycle and situation[i][0] < end_cycle: 46 | return False 47 | 48 | for i in range( len( tackle ) ): 49 | if tackle[i][0] > start_cycle and tackle[i][0] < end_cycle and gt.checkTackle( i, tackle, player_state_l, player_state_r ): 50 | return False 51 | 52 | # ------------------------------------- # 53 | 54 | return True 55 | 56 | def checkPassSuccess( ball, kick, miss, success, index, team ): 57 | 58 | start_cycle = kick[index][1] 59 | end_cycle = kick[index + 1][1] 60 | 61 | if team == "l": 62 | if not passChecker( kick, index, team ): 63 | miss[0] += 1 64 | if( ball[start_cycle - 1][0] > 36.0 and ball[start_cycle - 1][1] < 20.16 and ball[start_cycle - 1][1] > -20.16 ): 65 | miss[1] += 1 66 | elif ball[start_cycle - 1][0] > 17.5: 67 | miss[2] += 1 68 | elif ball[start_cycle - 1][0] > -17.5: 69 | miss[3] += 1 70 | else: 71 | miss[4] += 1 72 | 73 | return False 74 | else: 75 | success[0] += 1 76 | if( ball[start_cycle - 1][0] > 36.0 and ball[start_cycle - 1][1] < 20.16 and ball[start_cycle - 1][1] > -20.16 ): 77 | success[1] += 1 78 | elif ball[start_cycle - 1][0] > 17.5: 79 | success[2] += 1 80 | elif ball[start_cycle - 1][0] > -17.5: 81 | success[3] += 1 82 | else: 83 | success[4] += 1 84 | 85 | return True 86 | 87 | else: 88 | if not passChecker( kick, index, team ): 89 | miss[0] += 1 90 | if( ball[start_cycle - 1][0] < -36.0 and ball[start_cycle - 1][1] < 20.16 and ball[start_cycle - 1][1] > -20.16 ): 91 | miss[1] += 1 92 | elif ball[start_cycle - 1][0] < -17.5: 93 | miss[2] += 1 94 | elif ball[start_cycle - 1][0] < 17.5: 95 | miss[3] += 1 96 | else: 97 | miss[4] += 1 98 | 99 | return False 100 | else: 101 | success[0] += 1 102 | if( ball[start_cycle - 1][0] < -36.0 and ball[start_cycle - 1][1] < 20.16 and ball[start_cycle - 1][1] > -20.16 ): 103 | success[1] += 1 104 | elif ball[start_cycle - 1][0] < -17.5: 105 | success[2] += 1 106 | elif ball[start_cycle - 1][0] < 17.5: 107 | success[3] += 1 108 | else: 109 | success[4] += 1 110 | 111 | return True 112 | 113 | def passProbability( ball, kick, situation, tackle, player_state_l, player_state_r, team ): 114 | 115 | # success = [ pass_success, pass_success_pa, pass_success_at, pass_success_mt, pass_success_dt ] 116 | # miss = [ pass_miss, pass_miss_pa, pass_miss_at, pass_miss_mt, pass_miss_dt ] 117 | # probability = [ all, pa, at, mt, dt ] 118 | success = [] 119 | miss = [] 120 | probability = [] 121 | count = 0 # the number of pass 122 | 123 | 124 | for i in range( 5 ): 125 | success.append( 0 ) 126 | miss.append( 0 ) 127 | probability.append( 0 ) 128 | 129 | for i in range( len( kick ) ): 130 | 131 | if not exceptionPass( ball, kick, situation, tackle, player_state_l, player_state_r, i, team ): 132 | continue 133 | 134 | count += 1 135 | 136 | checkPassSuccess( ball, kick, miss, success, i, team ) 137 | 138 | for i in range( 5 ): 139 | if success[i] == 0: 140 | probability[i] = 0 141 | else: 142 | probability[i] = round( success[i] / float( success[i] + miss[i] ), 2 ) 143 | 144 | return probability 145 | -------------------------------------------------------------------------------- /extraction/rcg_reader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | 8 | 9 | def getInformation( tmp_line: list, wm: la_class.WorldModel ) -> None: 10 | 11 | getBallInformation( tmp_line, wm ) 12 | getLeftTeamInformation( tmp_line, wm ) 13 | getRightTeamInformation( tmp_line, wm ) 14 | 15 | 16 | def getBallInformation( tmp_line: list, wm: la_class.WorldModel ) -> None: 17 | 18 | wm.ball.pos.x = float( tmp_line[3] ) 19 | wm.ball.pos.y = float( tmp_line[4] ) 20 | wm.ball.vel.x = float( tmp_line[5] ) 21 | wm.ball.vel.y = float( tmp_line[6].strip(")") ) 22 | 23 | 24 | def getLeftTeamInformation( tmp_line: list, wm: la_class.WorldModel ) -> None: 25 | # get uniform number and geometrical informartion 26 | unum: cython.int = 0 27 | referred_i: cython.int = 0 28 | for i in range(len(tmp_line)): 29 | 30 | if tmp_line[i] == "((r": 31 | # skip 32 | break 33 | 34 | if "(" not in tmp_line[i]: 35 | # skip 36 | continue 37 | 38 | if tmp_line[i] == "((l": 39 | if referred_i > i: 40 | continue 41 | unum = int(tmp_line[i+1].replace(")", "")) - 1 42 | wm.l.player[unum].hetero = int(tmp_line[i+2]) 43 | wm.l.player[unum].state = tmp_line[i+3] 44 | wm.l.player[unum].pos.x = float(tmp_line[i+4]) 45 | wm.l.player[unum].pos.y = float(tmp_line[i+5]) 46 | wm.l.player[unum].vel.x = float(tmp_line[i+6]) 47 | wm.l.player[unum].vel.y = float(tmp_line[i+7]) 48 | wm.l.player[unum].body_angle = float(tmp_line[i+8]) 49 | wm.l.player[unum].neck_angle = float(tmp_line[i+9]) 50 | if tmp_line[i+10] == "(v": 51 | referred_i = i + 10 52 | continue 53 | else: 54 | wm.l.player[unum].pointing_target.x = float(tmp_line[i+10]) 55 | wm.l.player[unum].pointing_target.y = float(tmp_line[i+11]) 56 | referred_i = i + 12 57 | if tmp_line[i] == "(v": 58 | wm.l.player[unum].quality = tmp_line[i+1] 59 | wm.l.player[unum].view_area = int(tmp_line[i+2].replace(")", "")) 60 | referred_i = i + 2 61 | if tmp_line[i] == "(s": 62 | wm.l.player[unum].stamina = float(tmp_line[i+1]) 63 | wm.l.player[unum].effort = float(tmp_line[i+2]) 64 | wm.l.player[unum].recovery = float(tmp_line[i+3]) 65 | wm.l.player[unum].stamina_capacity = float(tmp_line[i+4].replace(")", "")) 66 | referred_i = i + 4 67 | if tmp_line[i] == "(f": 68 | wm.l.player[unum].focus_target = "{}_{}".format(tmp_line[i+1], tmp_line[i+2].replace(")", "")) 69 | referred_i = i + 2 70 | if tmp_line[i] == "(c": 71 | wm.l.player[unum].kick_count = int(tmp_line[i+1]) 72 | wm.l.player[unum].dash_count = int(tmp_line[i+2]) 73 | wm.l.player[unum].turn_count = int(tmp_line[i+3]) 74 | wm.l.player[unum].catch_count = int(tmp_line[i+4]) 75 | wm.l.player[unum].move_count = int(tmp_line[i+5]) 76 | wm.l.player[unum].turn_neck_count = int(tmp_line[i+6]) 77 | wm.l.player[unum].change_view_count = int(tmp_line[i+7]) 78 | wm.l.player[unum].say_count = int(tmp_line[i+8]) 79 | wm.l.player[unum].tackle_count = int(tmp_line[i+9]) 80 | wm.l.player[unum].arm_target_count = int(tmp_line[i+10]) 81 | wm.l.player[unum].attention_count = int(tmp_line[i+11].replace("))", "")) 82 | referred_i = i + 11 83 | 84 | 85 | 86 | def getRightTeamInformation( tmp_line: list, wm: la_class.WorldModel ) -> None: 87 | unum: cython.int = 0 88 | referred_i: cython.int = 0 89 | is_left_team: cython.bint = False 90 | 91 | # get uniform number and geometrical information 92 | for i in range(len(tmp_line)): 93 | 94 | if tmp_line[i] == "((l": 95 | is_left_team = True 96 | continue 97 | 98 | if "(" not in tmp_line[i]: 99 | # skip 100 | continue 101 | 102 | if tmp_line[i] == "((r": 103 | is_left_team = False 104 | if referred_i > i: 105 | continue 106 | unum = int(tmp_line[i+1].replace(")", "")) - 1 107 | wm.r.player[unum].hetero = int(tmp_line[i+2]) 108 | wm.r.player[unum].state = tmp_line[i+3] 109 | wm.r.player[unum].pos.x = float(tmp_line[i+4]) 110 | wm.r.player[unum].pos.y = float(tmp_line[i+5]) 111 | wm.r.player[unum].vel.x = float(tmp_line[i+6]) 112 | wm.r.player[unum].vel.y = float(tmp_line[i+7]) 113 | wm.r.player[unum].body_angle = float(tmp_line[i+8]) 114 | wm.r.player[unum].neck_angle = float(tmp_line[i+9]) 115 | if tmp_line[i+10] == "(v": 116 | referred_i = i + 10 117 | continue 118 | else: 119 | wm.r.player[unum].pointing_target.x = float(tmp_line[i+10]) 120 | wm.r.player[unum].pointing_target.y = float(tmp_line[i+11]) 121 | referred_i = i + 12 122 | if not is_left_team and tmp_line[i] == "(v": 123 | wm.r.player[unum].quality = tmp_line[i+1] 124 | wm.r.player[unum].view_area = int(tmp_line[i+2].replace(")", "")) 125 | referred_i = i + 2 126 | if not is_left_team and tmp_line[i] == "(s": 127 | wm.r.player[unum].stamina = float(tmp_line[i+1]) 128 | wm.r.player[unum].effort = float(tmp_line[i+2]) 129 | wm.r.player[unum].recovery = float(tmp_line[i+3]) 130 | wm.r.player[unum].stamina_capacity = float(tmp_line[i+4].replace(")", "")) 131 | referred_i = i + 4 132 | if not is_left_team and tmp_line[i] == "(f": 133 | wm.r.player[unum].focus_target = "{}_{}".format(tmp_line[i+1], tmp_line[i+2]) 134 | referred_i = i + 2 135 | if not is_left_team and tmp_line[i] == "(c": 136 | wm.r.player[unum].kick_count = int(tmp_line[i+1]) 137 | wm.r.player[unum].dash_count = int(tmp_line[i+2]) 138 | wm.r.player[unum].turn_count = int(tmp_line[i+3]) 139 | wm.r.player[unum].catch_count = int(tmp_line[i+4]) 140 | wm.r.player[unum].move_count = int(tmp_line[i+5]) 141 | wm.r.player[unum].turn_neck_count = int(tmp_line[i+6]) 142 | wm.r.player[unum].change_view_count = int(tmp_line[i+7]) 143 | wm.r.player[unum].say_count = int(tmp_line[i+8]) 144 | wm.r.player[unum].tackle_count = int(tmp_line[i+9]) 145 | wm.r.player[unum].arm_target_count = int(tmp_line[i+10]) 146 | wm.r.player[unum].attention_count = int(tmp_line[i+11].replace("))", "").replace(")", "")) 147 | referred_i = i + 11 148 | 149 | -------------------------------------------------------------------------------- /calculation/kick_distribution.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | # -*- coding: utf-8 -*- 4 | 5 | import cython 6 | 7 | from lib import lib_log_analyzer as lib 8 | from lib import la_class 9 | 10 | 11 | def saveKickDistribution( feat: la_class.Feature, path: str, degree_range: cython.int = 90 ) -> None: 12 | 13 | with open( path+'kick_distribution.csv', 'a' ) as f: 14 | 15 | file_pointers: list = [] 16 | for i in range(-180, 180, degree_range): 17 | fp = open("kick_distribution_{}_{}.csv".format(i, i+degree_range), "a") 18 | file_pointers.append(fp) 19 | f_success = open("kick_distribution_success.csv", "a") 20 | 21 | for i in range( len( feat.kick_sequence ) - 1 ): 22 | degree: cython.int = lib.changeRadianToDegree(lib.calcRadianC(feat.kick_sequence[i][1], feat.kick_sequence[i][2], 23 | feat.kick_sequence[i + 1][1], 24 | feat.kick_sequence[i + 1][2])) 25 | 26 | # if not terminal condition 27 | if ( feat.kick_sequence[i][4] != -1 \ 28 | and feat.kick_sequence[i+1][4] != 0 ): 29 | f.write( str( feat.kick_sequence[i][1] ) + ',' + 30 | str( feat.kick_sequence[i][2] ) + ',' + 31 | str( lib.calcDistC( feat.kick_sequence[i][1], 32 | feat.kick_sequence[i][2], 33 | feat.kick_sequence[i+1][1], 34 | feat.kick_sequence[i+1][2] ) ) + ',' + 35 | str(degree) + 36 | str('\n') ) 37 | 38 | if (feat.kick_sequence[i][3] == 1): 39 | f_success.write( str( feat.kick_sequence[i][1] ) + ',' + 40 | str( feat.kick_sequence[i][2] ) + ',' + 41 | str( lib.calcDistC( feat.kick_sequence[i][1], 42 | feat.kick_sequence[i][2], 43 | feat.kick_sequence[i+1][1], 44 | feat.kick_sequence[i+1][2] ) ) + ',' + 45 | str(degree) + 46 | str('\n') ) 47 | 48 | fp_ind = int((degree + 180) / degree_range) 49 | 50 | file_pointers[fp_ind].write( str( feat.kick_sequence[i][1] ) + ',' + 51 | str( feat.kick_sequence[i][2] ) + ',' + 52 | str( lib.calcDistC( feat.kick_sequence[i][1], 53 | feat.kick_sequence[i][2], 54 | feat.kick_sequence[i+1][1], 55 | feat.kick_sequence[i+1][2] ) ) + ',' + 56 | str(degree) + 57 | str('\n') ) 58 | 59 | for fp in file_pointers: 60 | fp.close() 61 | f_success.close() 62 | 63 | 64 | def printKickDistribution( sp: la_class.ServerParam, feat: la_class.Feature, path: str ) -> None: 65 | 66 | import matplotlib 67 | #matplotlib.use('Agg') 68 | from mpl_toolkits.mplot3d import Axes3D 69 | from matplotlib.patches import Circle 70 | import mpl_toolkits.mplot3d.art3d as art3d 71 | from matplotlib import pyplot as plt 72 | import matplotlib.font_manager as fm 73 | 74 | xlim: cython.double = sp.pitch_length / 2 + 5.0 75 | ylim: cython.double = sp.pitch_width / 2 + 5.0 76 | fm._rebuild() 77 | #plt.rc('font', family='Times New Roman') 78 | #plt.rcParams['font.family'] = 'Times New Roman' 79 | 80 | fig = plt.figure(figsize=(10.5, 6.8)) 81 | plt.rcParams['font.family'] = 'Times New Roman' 82 | plt.rcParams['ps.useafm'] = True 83 | plt.rcParams['pdf.use14corefonts'] = True 84 | plt.rcParams['text.usetex'] = True 85 | 86 | ax = fig.add_subplot( 111, projection='3d', azim = 240 ) 87 | 88 | ax.set_xlabel("{\it x}", fontsize=32, labelpad=20) 89 | ax.set_ylabel("{\it y}", fontsize=32, labelpad=20) 90 | ax.set_zlabel("{\it distance}", fontsize=32, labelpad=20) 91 | 92 | ax.set_xlim( -xlim, xlim ) 93 | ax.set_ylim( ylim, -ylim ) 94 | 95 | ax.tick_params(labelsize=32, pad=10) 96 | 97 | # plot soccer fields 98 | ax.plot3D( [ sp.goal_line_l, -sp.penalty_area_x ], [ -sp.penalty_area_y, -sp.penalty_area_y ], [ 0, 0 ], color='g', linewidth=4 ) 99 | ax.plot3D( [ sp.goal_line_l, -sp.penalty_area_x ], [ sp.penalty_area_y, sp.penalty_area_y ], [ 0, 0 ], color='g', linewidth=4 ) 100 | ax.plot3D( [ sp.goal_line_r, sp.penalty_area_x ], [ -sp.penalty_area_y, -sp.penalty_area_y ], [ 0, 0 ], color='g', linewidth=4 ) 101 | ax.plot3D( [ sp.goal_line_r, sp.penalty_area_x ], [ sp.penalty_area_y, sp.penalty_area_y ], [ 0, 0 ], color='g', linewidth=4 ) 102 | ax.plot3D( [ -sp.penalty_area_x, -sp.penalty_area_x ], [ sp.penalty_area_y, -sp.penalty_area_y ], [ 0, 0 ], color='g', linewidth=4 ) 103 | ax.plot3D( [ sp.penalty_area_x, sp.penalty_area_x ], [ sp.penalty_area_y, -sp.penalty_area_y ],[ 0, 0 ], color='g', linewidth=4 ) 104 | 105 | ax.plot3D( [ sp.goal_line_l, sp.goal_line_r ], [ -sp.pitch_width / 2, -sp.pitch_width / 2 ], [ 0, 0 ], color='g', linewidth=4 ) 106 | ax.plot3D( [ sp.goal_line_l, sp.goal_line_r ], [ sp.pitch_width / 2, sp.pitch_width / 2 ], [ 0, 0 ], color='g', linewidth=4 ) 107 | ax.plot3D( [ sp.goal_line_l, sp.goal_line_l ], [ -sp.pitch_width / 2, sp.pitch_width / 2 ], [ 0, 0 ], color='g', linewidth=4 ) 108 | ax.plot3D( [ sp.goal_line_r, sp.goal_line_r ], [ -sp.pitch_width / 2, sp.pitch_width / 2 ], [ 0, 0 ], color='g', linewidth=4 ) 109 | 110 | plt.plot( [ 0, 0 ], [ -sp.pitch_width / 2, sp.pitch_width / 2 ], color='g', linewidth=4 ) 111 | 112 | p = Circle((0.5, 0.5), 9, ec="g", fc="None", linewidth=4 ) 113 | ax.add_patch(p) 114 | art3d.pathpatch_2d_to_3d(p, z=0.0, zdir="z") 115 | 116 | # plot kick distribution 117 | for i in range( len( feat.kick_sequence ) - 1 ): 118 | # if not tarminal condition 119 | if ( feat.kick_sequence[i][4] != -1 \ 120 | and feat.kick_sequence[i+1][4] != 0 ): 121 | ax.plot3D( [ feat.kick_sequence[i][1], feat.kick_sequence[i][1] ], 122 | [ feat.kick_sequence[i][2], feat.kick_sequence[i][2] ], 123 | [ 0, lib.calcDistC( feat.kick_sequence[i][1], 124 | feat.kick_sequence[i][2], 125 | feat.kick_sequence[i+1][1], 126 | feat.kick_sequence[i+1][2] ) ], 127 | "red" ) 128 | 129 | filename: cython.str = path + feat.team_point[0] + "-kick_distribution" 130 | extension: list = [".eps", ".pdf", ".png", ".svg"] 131 | for e in extension: 132 | plt.savefig(filename+e, dpi=300, bbox_inches="tight", transparent=True) 133 | plt.show() 134 | -------------------------------------------------------------------------------- /calculation/pass_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import cython 5 | 6 | from lib import la_class 7 | from lib import lib_log_analyzer as lib 8 | 9 | 10 | def countKick( wm: list, cycle: cython.int, feat: la_class.Feature ) -> str: 11 | 12 | # same timing kick is already considered 13 | if ( feat.target_team == "l" ): 14 | if ( wm[cycle+1].dominate_side == "l" ): 15 | 16 | direction: cython.str = getPassRoute( wm, cycle ) 17 | 18 | if ( direction == "left" ): 19 | feat.our_kick[0] += 1 20 | elif ( direction == "right" ): 21 | feat.our_kick[1] += 1 22 | elif ( direction == "front" ): 23 | feat.our_kick[2] += 1 24 | elif ( direction == "back" ): 25 | feat.our_kick[3] += 1 26 | 27 | if ( wm[cycle+1].dominate_side == "r" ): 28 | 29 | direction: cython.str = getPassRoute( wm, cycle ) 30 | 31 | if ( direction == "left" ): 32 | feat.opp_kick[0] += 1 33 | elif ( direction == "right" ): 34 | feat.opp_kick[1] += 1 35 | elif ( direction == "front" ): 36 | feat.opp_kick[2] += 1 37 | elif ( direction == "back" ): 38 | feat.opp_kick[3] += 1 39 | 40 | elif ( feat.target_team == "r" ): 41 | if ( wm[cycle+1].dominate_side == "l" ): 42 | 43 | direction: cython.str = getPassRoute( wm, cycle ) 44 | 45 | if ( direction == "left" ): 46 | feat.opp_kick[0] += 1 47 | elif ( direction == "right" ): 48 | feat.opp_kick[1] += 1 49 | elif ( direction == "front" ): 50 | feat.opp_kick[2] += 1 51 | elif ( direction == "back" ): 52 | feat.opp_kick[3] += 1 53 | 54 | if ( wm[cycle+1].dominate_side == "r" ): 55 | 56 | direction: cython.str = getPassRoute( wm, cycle ) 57 | 58 | if ( direction == "left" ): 59 | feat.our_kick[0] += 1 60 | elif ( direction == "right" ): 61 | feat.our_kick[1] += 1 62 | elif ( direction == "front" ): 63 | feat.our_kick[2] += 1 64 | elif ( direction == "back" ): 65 | feat.our_kick[3] += 1 66 | 67 | return direction 68 | 69 | 70 | def countPass( wm: list, cycle: cython.int, direction: str, feat: la_class.Feature ) -> None: 71 | 72 | if ( wm[cycle+1].dominate_side == wm[cycle].dominate_side \ 73 | and wm[cycle+1].last_kicker_unum != wm[cycle].last_kicker_unum ): 74 | 75 | if ( wm[cycle+1].dominate_side == feat.target_team ): 76 | 77 | if ( direction == "left" ): 78 | if ( not __debug__ ): 79 | print ( "our", wm[cycle].last_kicker_unum+1, \ 80 | "left pass:", wm[cycle].last_kicked_cycle, \ 81 | "to", wm[cycle+1].last_kicker_unum+1, \ 82 | "received:", wm[cycle+1].last_kicked_cycle ) 83 | feat.our_pass[0] += 1 84 | elif ( direction == "right" ): 85 | if ( not __debug__ ): 86 | print ( "our", wm[cycle].last_kicker_unum+1, \ 87 | "right pass:", wm[cycle].last_kicked_cycle, \ 88 | "to", wm[cycle+1].last_kicker_unum+1, \ 89 | "received:", wm[cycle+1].last_kicked_cycle ) 90 | feat.our_pass[1] += 1 91 | elif ( direction == "front" ): 92 | if ( not __debug__ ): 93 | print ( "our", wm[cycle].last_kicker_unum+1, \ 94 | "front pass:", wm[cycle].last_kicked_cycle, \ 95 | "to", wm[cycle+1].last_kicker_unum+1, \ 96 | "received:", wm[cycle+1].last_kicked_cycle ) 97 | feat.our_pass[2] += 1 98 | elif ( direction == "back" ): 99 | if ( not __debug__ ): 100 | print ( "our", wm[cycle].last_kicker_unum+1, \ 101 | "back pass:", wm[cycle].last_kicked_cycle, \ 102 | "to", wm[cycle+1].last_kicker_unum+1, \ 103 | "received:", wm[cycle+1].last_kicked_cycle ) 104 | feat.our_pass[3] += 1 105 | 106 | else: 107 | if ( direction == "left" ): 108 | if ( not __debug__ ): 109 | print ( "opp", wm[cycle].last_kicker_unum+1, \ 110 | "left pass:", wm[cycle].last_kicked_cycle, \ 111 | "to", wm[cycle+1].last_kicker_unum+1, \ 112 | "received:", wm[cycle+1].last_kicked_cycle ) 113 | feat.opp_pass[0] += 1 114 | elif ( direction == "right" ): 115 | if ( not __debug__ ): 116 | print ( "opp", wm[cycle].last_kicker_unum+1, \ 117 | "right pass:", wm[cycle].last_kicked_cycle, \ 118 | "to", wm[cycle+1].last_kicker_unum+1, \ 119 | "received:", wm[cycle+1].last_kicked_cycle ) 120 | feat.opp_pass[1] += 1 121 | elif ( direction == "front" ): 122 | if ( not __debug__ ): 123 | print ( "opp", wm[cycle].last_kicker_unum+1, \ 124 | "front pass:", wm[cycle].last_kicked_cycle, \ 125 | "to", wm[cycle+1].last_kicker_unum+1, \ 126 | "received:", wm[cycle+1].last_kicked_cycle ) 127 | feat.opp_pass[2] += 1 128 | elif ( direction == "back" ): 129 | if ( not __debug__ ): 130 | print ( "opp", wm[cycle].last_kicker_unum+1, \ 131 | "back pass:", wm[cycle].last_kicked_cycle, \ 132 | "to", wm[cycle+1].last_kicker_unum+1, \ 133 | "received:", wm[cycle+1].last_kicked_cycle ) 134 | feat.opp_pass[3] += 1 135 | 136 | 137 | def getPassRoute( wm: list, cycle:cython.int ) -> str: 138 | 139 | # check pass route. return -> left, right, front, back 140 | last_kicked_cycle: cython.int = wm[cycle].last_kicked_cycle 141 | radian: cython.double = lib.calcRadian( wm[ last_kicked_cycle ].ball.pos, wm[ cycle ].ball.pos ) 142 | degree: cython.double = lib.changeRadianToDegree( radian ) 143 | 144 | if( wm[cycle+1].dominate_side == "l" ): 145 | 146 | if( degree > -45.0 and degree <= 45.0 ): 147 | return "front" 148 | elif( degree > 45.0 and degree <= 135.0 ): 149 | return "right" 150 | elif( degree > 135.0 or degree <= -135.0 ): 151 | return "back" 152 | elif( degree > -135.0 and degree <= -45.0 ): 153 | return "left" 154 | 155 | elif ( wm[cycle+1].dominate_side == "r" ): 156 | 157 | if( degree > -45.0 and degree <= 45.0 ): 158 | return "back" 159 | elif( degree > 45.0 and degree <= 135.0 ): 160 | return "left" 161 | elif( degree > 135.0 or degree <= -135.0 ): 162 | return "front" 163 | elif( degree > -135.0 and degree <= -45.0 ): 164 | return "right" 165 | -------------------------------------------------------------------------------- /calculation/calculate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import os 5 | import cython 6 | import argparse 7 | 8 | from . import offside_line as ol 9 | 10 | from . import pass_check as pc 11 | from . import through_pass as tp 12 | from . import shoot 13 | from . import dribble_count as dc 14 | from . import kick_sequence as ks 15 | from . import kick_distribution as kd 16 | 17 | from extraction import referee 18 | from extraction import get_tackle as gt 19 | from extraction import get_kick as gk 20 | 21 | from lib import state 22 | from lib import la_class 23 | 24 | 25 | def analyzeLog(args: argparse.Namespace, wm: list, sp: la_class.ServerParam, feat: la_class.Feature) -> None: 26 | # write index 27 | fname: cython.str = '{}{}.csv'.format(args.output_dir, feat.team_point[0]) 28 | if not os.path.exists(fname): 29 | feat.outputIndexForR(fname) 30 | 31 | ol.calcOffsideLine(args, wm) 32 | 33 | for cycle in range(args.start_cycle, args.end_cycle): 34 | 35 | relative_cycle: cython.int = cycle - args.start_cycle 36 | 37 | # output all information in csv format (ignore extra halves) 38 | if args.game and cycle != 0 and cycle <= 6000: 39 | wm[relative_cycle].outputInfo('{}all_info.csv'.format(args.output_dir)) 40 | 41 | for say in wm[relative_cycle].referee.say: 42 | if ("offside" in say 43 | or "free_kick_" in say 44 | or "kick_in_" in say 45 | or "before_kick_off" in say 46 | or "kick_off" in say 47 | or "foul_charge_" in say 48 | or "goal_kick_" in say 49 | or "goalie_catch_ball_" in say 50 | or "corner_kick_" in say 51 | or "drop_ball" in say): 52 | wm[relative_cycle].dominate_side = "neutral" 53 | wm[relative_cycle].referee.said = True 54 | 55 | elif "goal_l" in say: 56 | wm[relative_cycle].dominate_side = "neutral" 57 | wm[relative_cycle].referee.said = True 58 | if not __debug__: 59 | print("team l goal!") 60 | if feat.target_team == "l": 61 | feat.our_point += 1 62 | elif feat.target_team == "r": 63 | feat.opp_point += 1 64 | 65 | elif "goal_r" in say: 66 | wm[relative_cycle].dominate_side = "neutral" 67 | wm[relative_cycle].referee.said = True 68 | if not __debug__: 69 | print("team r goal!") 70 | if feat.target_team == "r": 71 | feat.our_point += 1 72 | elif feat.target_team == "l": 73 | feat.opp_point += 1 74 | 75 | if (wm[relative_cycle].dominate_side == "l" 76 | and wm[relative_cycle].referee.playmode == "play_on"): 77 | if feat.target_team == "l": 78 | feat.our_dominate_time += 1 79 | if feat.target_team == "r": 80 | feat.opp_dominate_time += 1 81 | 82 | if (wm[relative_cycle].dominate_side == "r" 83 | and wm[relative_cycle].referee.playmode == "play_on"): 84 | if feat.target_team == "r": 85 | feat.our_dominate_time += 1 86 | if feat.target_team == "l": 87 | feat.opp_dominate_time += 1 88 | 89 | try: 90 | feat.our_possession = float(feat.our_dominate_time / float(feat.our_dominate_time + feat.opp_dominate_time)) 91 | feat.opp_possession = float(feat.opp_dominate_time / float(feat.our_dominate_time + feat.opp_dominate_time)) 92 | except ZeroDivisionError: 93 | feat.our_possession = 0.0 94 | feat.opp_possession = 0.0 95 | 96 | feat.our_yellow += referee.countOurYellowCard(wm[relative_cycle], feat.target_team) 97 | feat.opp_yellow += referee.countOppYellowCard(wm[relative_cycle], feat.target_team) 98 | 99 | feat.all_our_tackle += gt.countOurTackle(wm[relative_cycle], feat.target_team) 100 | feat.all_opp_tackle += gt.countOppTackle(wm[relative_cycle], feat.target_team) 101 | feat.our_tackle += gt.countOurSuccessTackle(wm[relative_cycle], wm[relative_cycle + 1], 102 | feat.target_team) 103 | feat.opp_tackle += gt.countOppSuccessTackle(wm[relative_cycle], wm[relative_cycle + 1], 104 | feat.target_team) 105 | 106 | kick_count: cython.int = gk.isKick(wm, relative_cycle) 107 | 108 | # simultaneous kick 109 | if kick_count > 1: 110 | ks.considerSimultaneousKick(wm, relative_cycle, feat, kick_dist_thr=0.0) 111 | # finish kick sequence by opp intercept 112 | if not __debug__ and not wm[relative_cycle].referee.said: 113 | if wm[relative_cycle].dominate_side == "l": 114 | print("team r Intercept", cycle + 1) 115 | elif wm[relative_cycle].dominate_side == "r": 116 | print("team l Intercept", cycle + 1) 117 | # finish kick sequence by penalty and play stopped 118 | if not __debug__ and wm[relative_cycle].referee.said: 119 | # should be considered 120 | if wm[relative_cycle - 1].dominate_side == "l": 121 | print("team r Penalty", cycle + 1) 122 | elif wm[relative_cycle - 1].dominate_side == "r": 123 | print("team l Penalty", cycle + 1) 124 | elif (kick_count == 1 125 | and not wm[relative_cycle].referee.said): 126 | if shoot.isOurShoot(wm[relative_cycle + 1], sp, cycle, feat.target_team): 127 | feat.our_shoot += 1 128 | elif shoot.isOppShoot(wm[relative_cycle + 1], sp, cycle, feat.target_team): 129 | feat.opp_shoot += 1 130 | direction: cython.str = pc.countKick(wm, relative_cycle, feat) 131 | pc.countPass(wm, relative_cycle, direction, feat) 132 | tp.countThroughPass(wm, relative_cycle, feat) 133 | dc.countDribble(wm, relative_cycle, feat) 134 | feat.our_penalty_area += ks.getSequence(wm, sp, relative_cycle, feat, 135 | until_penalty_area=True, kick_dist_thr=0.0) 136 | 137 | # find disconnected player 138 | l_disconnected_player: cython.int = 0 139 | r_disconnected_player: cython.int = 0 140 | for unum in range(11): 141 | if state.isDead(relative_cycle, unum, 'l', wm): 142 | l_disconnected_player += 1 143 | elif state.isDead(relative_cycle, unum, 'r', wm): 144 | r_disconnected_player += 1 145 | if feat.target_team == 'l': 146 | feat.our_disconnected_player = max(l_disconnected_player, feat.our_disconnected_player) 147 | feat.opp_disconnected_player = max(r_disconnected_player, feat.opp_disconnected_player) 148 | elif feat.target_team == 'r': 149 | feat.our_disconnected_player = max(r_disconnected_player, feat.our_disconnected_player) 150 | feat.opp_disconnected_player = max(l_disconnected_player, feat.opp_disconnected_player) 151 | 152 | # output for each cycle 153 | if args.each_cycle: 154 | fname_for_integrate_result = '{}{}-{}.csv'.format(args.output_dir, args.start_cycle+1, cycle+1) 155 | if not os.path.exists(fname_for_integrate_result): 156 | feat.outputIndexForIR(fname_for_integrate_result) 157 | feat.outputIntegrateResult(fname_for_integrate_result) 158 | 159 | feat.outputResult( fname ) 160 | # ks.saveKickSequence( feat, args.output_dir, outputKickedCycle=True ) 161 | # kd.saveKickDistribution( feat, args.output_dir, degree_range=90 ) 162 | 163 | 164 | if not __debug__: 165 | ks.printSequence(sp, feat, args.output_dir) 166 | kd.printKickDistribution(sp, feat, args.output_dir) 167 | -------------------------------------------------------------------------------- /calculation/kick_sequence.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | # -*- coding: utf-8 -*- 4 | 5 | import cython 6 | import itertools 7 | 8 | from lib import lib_log_analyzer as lib 9 | from lib import la_class 10 | 11 | def appendSequence( feat: la_class.Feature, color: str, kick_dist_thr: cython.double ) -> None: 12 | INVALID_VALUE: cython.double = -10000.0 13 | 14 | START_SEQUENCE: cython.int = 0 15 | BETWEEN_SEQUENCE: cython.int = 1 16 | END_SEQUENCE: cython.int = -1 17 | 18 | POSITIVE_LABEL: cython.int = 1 19 | NEGATIVE_LABEL: cython.int = 0 20 | 21 | # ignore the kick_paths whose distance is shorter than kick_dist_thr 22 | tmp_kick_path_x: list = [] 23 | tmp_kick_path_y: list = [] 24 | 25 | # kick_sequence: ( [ kick_cycle, x, y, teacher signal, sequence flag, kicker_unum, receiver_unum, ..., 26 | # mate1_x, mate1_y, mate1_bodyangle, mate1_neckangle, ... , opp1_x, opp1_y, opp1_bodyangle, opp1_neckangle, ... ] ) 27 | for i in range(len(feat.kick_path_x)): 28 | sequence_flag: cython.int = END_SEQUENCE if i == len(feat.kick_path_x) - 1 \ 29 | else START_SEQUENCE if i == 0 \ 30 | else BETWEEN_SEQUENCE 31 | label: cython.int = POSITIVE_LABEL if color == "ro-" \ 32 | else NEGATIVE_LABEL if color == "bo--" \ 33 | else -1 34 | if label == -1: 35 | raise SyntaxError 36 | if sequence_flag == BETWEEN_SEQUENCE: 37 | if lib.calcDistC(feat.kick_path_x[i], feat.kick_path_y[i], feat.kick_path_x[i-1], feat.kick_path_y[i-1]) < kick_dist_thr: 38 | continue 39 | 40 | tmp_kick_path_x.append(feat.kick_path_x[i]) 41 | tmp_kick_path_y.append(feat.kick_path_y[i]) 42 | 43 | tmp_kick_sequence: list = [feat.kick_cycle[i], 44 | feat.kick_path_x[i], feat.kick_path_y[i], 45 | label, sequence_flag, 46 | feat.kicker[i], feat.receiver[i]] 47 | 48 | if sequence_flag == END_SEQUENCE: 49 | # for teammate 50 | tmp_kick_sequence.extend([INVALID_VALUE for _ in range(11 * 4)]) 51 | # for opponent 52 | tmp_kick_sequence.extend([INVALID_VALUE for _ in range(11 * 4)]) 53 | else: 54 | # for teammate 55 | tmp_kick_sequence.extend( 56 | list(itertools.chain.from_iterable( 57 | [feat.teammate_from_ball[i][j].pos.x, feat.teammate_from_ball[i][j].pos.y, 58 | feat.teammate_from_ball[i][j].body_angle, feat.teammate_from_ball[i][j].neck_angle, 59 | ] for j in range(11)))) 60 | # for opponent 61 | tmp_kick_sequence.extend( 62 | list(itertools.chain.from_iterable( 63 | [feat.opponent_from_ball[i][j].pos.x, feat.opponent_from_ball[i][j].pos.y, 64 | feat.opponent_from_ball[i][j].body_angle, feat.opponent_from_ball[i][j].neck_angle, 65 | ] for j in range(11)))) 66 | 67 | feat.kick_sequence.append(tmp_kick_sequence) 68 | 69 | # used for plotting kick_sequences 70 | feat.all_kick_path_x.append( tmp_kick_path_x ) 71 | feat.all_kick_path_y.append( tmp_kick_path_y ) 72 | feat.color4plt_ks.append(color) 73 | # feat.color4plt_ks.append('ro-') 74 | 75 | 76 | def printSequence(sp: la_class.ServerParam, feat: la_class.Feature, path: str) -> None: 77 | import matplotlib 78 | # smatplotlib.use('Agg') 79 | from matplotlib import pyplot as plt 80 | import matplotlib.font_manager as fm 81 | 82 | fig = plt.figure(figsize=(10.5, 6.8)) 83 | plt.rcParams['font.family'] = 'Times New Roman' 84 | plt.rcParams['ps.useafm'] = True 85 | plt.rcParams['pdf.use14corefonts'] = True 86 | plt.rcParams['text.usetex'] = True 87 | 88 | xlim = sp.pitch_length / 2 + 5.0 89 | ylim = sp.pitch_width / 2 + 5.0 90 | fm._rebuild() 91 | 92 | plt.xlim([-xlim, xlim]) 93 | plt.ylim([ylim, -ylim]) 94 | 95 | plt.tick_params(labelsize=32) 96 | 97 | # plot soccer fields 98 | plt.plot([sp.goal_line_l, -sp.penalty_area_x], [-sp.penalty_area_y, -sp.penalty_area_y], color='g', linewidth=4) 99 | plt.plot([sp.goal_line_l, -sp.penalty_area_x], [sp.penalty_area_y, sp.penalty_area_y], color='g', linewidth=4) 100 | plt.plot([sp.goal_line_r, sp.penalty_area_x], [-sp.penalty_area_y, -sp.penalty_area_y], color='g', linewidth=4) 101 | plt.plot([sp.goal_line_r, sp.penalty_area_x], [sp.penalty_area_y, sp.penalty_area_y], color='g', linewidth=4) 102 | plt.plot([-sp.penalty_area_x, -sp.penalty_area_x], [sp.penalty_area_y, -sp.penalty_area_y], color='g', linewidth=4) 103 | plt.plot([sp.penalty_area_x, sp.penalty_area_x], [sp.penalty_area_y, -sp.penalty_area_y], color='g', linewidth=4) 104 | 105 | plt.plot([sp.goal_line_l, sp.goal_line_r], [-sp.pitch_width / 2, -sp.pitch_width / 2], color='g', linewidth=4) 106 | plt.plot([sp.goal_line_l, sp.goal_line_r], [sp.pitch_width / 2, sp.pitch_width / 2], color='g', linewidth=4) 107 | plt.plot([sp.goal_line_l, sp.goal_line_l], [-sp.pitch_width / 2, sp.pitch_width / 2], color='g', linewidth=4) 108 | plt.plot([sp.goal_line_r, sp.goal_line_r], [-sp.pitch_width / 2, sp.pitch_width / 2], color='g', linewidth=4) 109 | 110 | plt.plot([0, 0], [-sp.pitch_width / 2, sp.pitch_width / 2], color='g', linewidth=4) 111 | p = plt.Circle((0.0, 0.0), 9.0, color='g', linewidth=4, fill=False) 112 | 113 | ax = plt.gca() 114 | ax.add_patch(p) 115 | 116 | for x, y, color in zip(feat.all_kick_path_x, feat.all_kick_path_y, feat.color4plt_ks): 117 | plt.plot(x, y, color) 118 | 119 | filename = path + feat.team_point[0] + "-kick_sequence" 120 | extension = [".eps", ".pdf", ".png", ".svg"] 121 | for e in extension: 122 | plt.savefig(filename + e, dpi=300, bbox_inches="tight", transparent=True) 123 | plt.show() 124 | 125 | 126 | def finishSequence( feat: la_class.Feature, color: str, kick_dist_thr: cython.double ) -> None: 127 | appendSequence( feat, color, kick_dist_thr=kick_dist_thr ) 128 | clearList( feat ) 129 | 130 | 131 | def clearList(feat: la_class.Feature) -> None: 132 | feat.kick_cycle = [] 133 | feat.kick_path_x = [] 134 | feat.kick_path_y = [] 135 | feat.kicker = [] 136 | feat.receiver = [] 137 | feat.teammate_from_ball = [] 138 | feat.opponent_from_ball = [] 139 | 140 | 141 | def getSequence( wm: list, sp: la_class.ServerParam, cycle: cython.int, feat: la_class.Feature, until_penalty_area: cython.bint = True, kick_dist_thr: cython.double = 3.0 ) -> cython.int: 142 | last_kick_side: cython.str = wm[cycle].dominate_side 143 | last_kick_cycle: cython.int = wm[cycle].last_kicked_cycle - 1 144 | 145 | kick_side: cython.str = wm[cycle + 1].dominate_side 146 | kick_cycle: cython.int = cycle 147 | 148 | NO_KICK: cython.int = -1 149 | NO_KICKER: cython.int = 0 150 | NO_RECEIVER: cython.int = 0 151 | 152 | if (kick_side == feat.target_team): 153 | 154 | kicked_from: cython.int = wm[cycle].last_kicker_unum + 1 155 | kicked_to: cython.int = wm[cycle + 1].last_kicker_unum + 1 156 | 157 | # Exit situations 158 | for i in range(last_kick_cycle, kick_cycle): 159 | 160 | for say in wm[i].referee.say: 161 | # considering own goal 162 | # last kick: our, kick_off: our -> own goal 163 | if ( "goal_" in say 164 | and not "goal_kick_" in say ): 165 | finishSequence( feat, "bo--", kick_dist_thr=kick_dist_thr ) 166 | return 0 167 | 168 | # attention to ball 169 | feat.kick_cycle.append(kick_cycle) 170 | feat.kick_path_x.append(wm[cycle].ball.pos.x) 171 | feat.kick_path_y.append(wm[cycle].ball.pos.y) 172 | if feat.target_team == "l": 173 | feat.teammate_from_ball.append(lib.sortPlayerUnumFromPos(wm[cycle].l.player, wm[cycle].ball.pos)) 174 | feat.opponent_from_ball.append(lib.sortPlayerUnumFromPos(wm[cycle].r.player, wm[cycle].ball.pos)) 175 | elif feat.target_team == "r": 176 | feat.teammate_from_ball.append(lib.sortPlayerUnumFromPos(wm[cycle].r.player, wm[cycle].ball.pos)) 177 | feat.opponent_from_ball.append(lib.sortPlayerUnumFromPos(wm[cycle].l.player, wm[cycle].ball.pos)) 178 | 179 | # start sequence 180 | if last_kick_side != feat.target_team: 181 | feat.kicker.append(NO_KICKER) 182 | feat.receiver.append(kicked_to) 183 | # continue sequence 184 | else: 185 | feat.kicker.append(kicked_from) 186 | feat.receiver.append(kicked_to) 187 | 188 | # Exit situations 189 | if feat.target_team == "l": 190 | if (wm[cycle].ball.pos.x > sp.penalty_area_x 191 | and abs(wm[cycle].ball.pos.y) < sp.penalty_area_y): 192 | 193 | # isSequence 194 | if len( feat.kick_path_x ) > 1: 195 | finishSequence( feat, "ro-", kick_dist_thr=kick_dist_thr ) if until_penalty_area else None 196 | return 1 197 | 198 | # notSequence 199 | else: 200 | clearList(feat) 201 | 202 | elif feat.target_team == "r": 203 | if (wm[cycle].ball.pos.x < - sp.penalty_area_x 204 | and abs(wm[cycle].ball.pos.y) < sp.penalty_area_y): 205 | 206 | # isSequence 207 | if len( feat.kick_path_x ) > 1: 208 | finishSequence( feat, "ro-", kick_dist_thr=kick_dist_thr ) if until_penalty_area else None 209 | return 1 210 | 211 | # notSequence 212 | else: 213 | clearList(feat) 214 | 215 | # Exit situations 216 | elif kick_side != feat.target_team: 217 | 218 | kicked_from: cython.int = wm[cycle].last_kicker_unum + 1 219 | kicked_to: cython.int = 0 220 | 221 | for i in range(last_kick_cycle, kick_cycle): 222 | 223 | # is sequence 224 | if len(feat.kick_path_x) > 0: 225 | for say in wm[i].referee.say: 226 | # goal from out of target area 227 | if "goal_" + feat.target_team in say: 228 | feat.kick_cycle.append(NO_KICK) 229 | feat.kick_path_x.append(wm[i].ball.pos.x) 230 | feat.kick_path_y.append(wm[i].ball.pos.y) 231 | feat.kicker.append(kicked_from) 232 | feat.receiver.append(NO_RECEIVER) 233 | finishSequence(feat, "ro-", kick_dist_thr=kick_dist_thr) 234 | return 1 235 | 236 | # goal_kick or corner_kick 237 | # sequence end when ball leaves the pitch 238 | if( "goal_kick_" in say 239 | or "corner_kick_" in say 240 | or "half_time" in say): 241 | feat.kick_cycle.append( NO_KICK ) 242 | feat.kick_path_x.append( wm[i-1].ball.pos.x ) 243 | feat.kick_path_y.append( wm[i-1].ball.pos.y ) 244 | feat.kicker.append( kicked_from ) 245 | feat.receiver.append( NO_RECEIVER ) 246 | finishSequence( feat, "bo--", kick_dist_thr=kick_dist_thr ) 247 | return 0 248 | 249 | # goalie catch 250 | if "goalie_catch_" in say: 251 | feat.kick_cycle.append(NO_KICK) 252 | feat.kick_path_x.append(wm[i].ball.pos.x) 253 | feat.kick_path_y.append(wm[i].ball.pos.y) 254 | feat.kicker.append(kicked_from) 255 | feat.receiver.append(NO_RECEIVER) 256 | finishSequence(feat, "bo--", kick_dist_thr=kick_dist_thr) 257 | return 0 258 | 259 | # isSequence and intercepted 260 | if ( len( feat.kick_path_x ) > 0 ): 261 | feat.kick_cycle.append( kick_cycle ) 262 | feat.kick_path_x.append( wm[ cycle ].ball.pos.x ) 263 | feat.kick_path_y.append( wm[ cycle ].ball.pos.y ) 264 | feat.kicker.append( kicked_from ) 265 | feat.receiver.append( NO_RECEIVER ) 266 | finishSequence( feat, "bo--", kick_dist_thr=kick_dist_thr ) 267 | 268 | return 0 269 | 270 | 271 | def considerSimultaneousKick(wm: list, cycle: cython.int, feat: la_class.Feature, kick_dist_thr: cython.double = 3.0) -> None: 272 | if (len(feat.kick_path_x) > 0): 273 | feat.kick_cycle.append(cycle) 274 | feat.kick_path_x.append(wm[cycle].ball.pos.x) 275 | feat.kick_path_y.append(wm[cycle].ball.pos.y) 276 | feat.kicker.append(wm[cycle].last_kicker_unum + 1) 277 | feat.receiver.append(0) 278 | finishSequence(feat, "bo--", kick_dist_thr=kick_dist_thr) 279 | 280 | 281 | def saveKickSequence(feat: la_class.Feature, path: str, outputKickedCycle: cython.bint = False) -> None: 282 | # kick_sequence data 283 | # named 'nn_kick_data.csv' before 284 | 285 | with open(path+'kick_sequence.csv', 'a') as f: 286 | for i in range(len(feat.kick_sequence)): 287 | if outputKickedCycle: 288 | f.write(str(feat.kick_sequence[i][0]) + ",") 289 | for j in range(1, len(feat.kick_sequence[i])): 290 | f.write(str(feat.kick_sequence[i][j])) 291 | if j == len(feat.kick_sequence[i]) - 1: 292 | f.write("\n") 293 | else: 294 | f.write(",") 295 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2021-10-22 Takuya Fukushima 2 | 3 | * main.py: 4 | - --output-dirオプション時にディレクトリを作成するように設定 5 | 6 | * requirements.txt: 7 | - pip install -r requirements.txt で環境をインストールできるように設定 8 | 9 | 2021-08-02 Takuya Fukushima 10 | 11 | * main.py: 12 | - エラー検出時止まらないように設定 13 | 14 | 2020-12-11 Takuya Fukushima 15 | 16 | * calculation/*: 17 | * extraction/*: 18 | * lib/*: 19 | - cython化動作の修正,高速化に対応 20 | - pydファイルの削除 21 | 22 | 2020-12-11 Takuya Fukushima 23 | 24 | * calculation/calculate.py: 25 | * calculation/kick_distribution.py: 26 | * calculation/kick_sequence.py: 27 | * extraction/extract.pyd: 28 | * extraction/extract.py: 29 | * extraction/filename_split.py: 30 | * extraction/rcg_reader.py: 31 | * lib/la_class.py: 32 | * lib/la_class.pyd: 33 | * lib/option.py: 34 | - --outputオプションの追加.出力ファイルのディレクトリを指定できるように変更 35 | 36 | * loganalyzer3: 37 | * loop.sh: 38 | * main.py: 39 | - --without-indexオプションの削除 40 | - loop.shを使わない実装に変更 41 | 42 | * clean.sh: 43 | - cythonファイル削除用スクリプトの追加 44 | 45 | 2020-10-17 Takuya Fukushima 46 | 47 | * calculation/calculate.py: 48 | - disconnected_playerの検出バグを修正 49 | * calculation/kick_sequence.py: 50 | - simultaneous kickに命名を変更 51 | 52 | 2020-07-22 Takuya Fukushima 53 | 54 | * calculation/calculate.py: 55 | - kick_dist_thrを0.0に設定 56 | * calculation/kick_distribution.py: 57 | - キック方向を出力に追加 58 | 59 | 2020-05-02 Takuya Fukushima 60 | 61 | * extraction/extract.py: 62 | * extraction/filename_split.py: 63 | * lib/la_class.py: 64 | * lib/option.py: 65 | * calculation/calculate.py: 66 | - ペナルティシュートアウトを含むログファイルに対応 67 | - ボール支配時間のIndex名を変更(dominate_time→domination_time) 68 | - ボールポゼッションの定義を変更(支配時間/総時間→支配時間/(支配時間+敵支配時間)) 69 | 70 | * lib/lib_log_analyzer.py: 71 | - 不要な関数を削除 72 | - 最終試合結果出力を0,1から勝ち点に変更(勝3,引1,負0) 73 | 74 | 2020-05-01 Takuya Fukushima 75 | 76 | * calculation/kick_sequence.py: 77 | * calculation/calculate.py: 78 | - キックシーケンス抽出部分の改良 79 | 80 | * build.sh: 81 | - build用のシェルスクリプトを追加(clear用のものは削除) 82 | 83 | 2020-04-30 Takuya Fukushima 84 | 85 | * calculation/kick_sequence.py: 86 | - キックシーケンスの出力を追加.ボディアングルとネックアングルを出力するように. 87 | * extraction/extract.py: 88 | - リファクタリング 89 | 90 | 2020-03-07 Takuya Fukushima 91 | 92 | * calculation/*.pxd, *.py: 93 | * extraction/*.pxd, *.py: 94 | * lib/*.pxd, *.py: 95 | * setup.py: 96 | * clean.sh: 97 | - cythonに対応 98 | 99 | * calculation/kick_sequence.py: 100 | - 距離が閾値以下のものはキックシーケンスに加えないように設定 101 | 102 | * README.md: 103 | - 更新 104 | 105 | 2020-03-05 Takuya Fukushima 106 | 107 | * extraction/extract.py: 108 | - rcgファイルから試合結果を取ってくる部分を修正 109 | 110 | * extraction/calculate.py: 111 | - キックシーケンス取得時における選手位置のクリアができていなかった 112 | バグを修正 113 | 114 | 2020-02-19 Takuya Fukushima 115 | 116 | * calculation/calculate.py: 117 | * calculation/kick_sequence.py: 118 | * lib/la_class.py: 119 | - キックシーケンスに味方位置情報を追加 120 | 121 | * loop.sh: 122 | エラー出力を出さないように修正 123 | 124 | 2019-06-27 Takuya Fukushima 125 | 126 | * .gitignore: 127 | - gitignoreの追加 128 | 129 | * calculation/calculate.py: 130 | * calculation/kick_sequence.py: 131 | - goalie catchの動作不良を解消 132 | - 敵行動軌跡の出力に対応 133 | 134 | * loop.sh: 135 | - 圧縮ファイルのループに対応 136 | 137 | 2019-06-27 Takuya Fukushima 138 | 139 | * calculation/calculate.py: 140 | * calculation/dribble_count.py: 141 | * calculation/kick_sequence.py: 142 | * calculation/pass_check.py: 143 | * calculation/through_pass.py: 144 | * extraction/extract.py: 145 | * extraction/filename_split.py: 146 | * lib/la_class.py: 147 | * lib/lib_log_analyzer.py: 148 | * main.py: 149 | - 圧縮ファイル(.gz)でも読み込み可能に実装. 150 | - ファイル名からではなくrcgファイルから試合結果を取ってくるように変更. 151 | - 上記変更に伴い,各参照ファイルを修正 152 | 153 | 2019-04-28 Takuya Fukushima 154 | 155 | * calculation/calculate.py: 156 | * calculation/kick_distribution.py: 157 | - リモートで実行できる形で実装を変更 158 | 159 | * lib/la_class.py: 160 | * lib/state.py: 161 | * calculation/calculate.py: 162 | - [WIP]敵のペナルティエリア侵入回数項目を追加 163 | - プレイヤのDisconnectedを出力項目に追加 164 | 165 | 2019-04-27 Takuya Fukushima 166 | 167 | * calculation/kick_distribution.py: 168 | * calculation/kick_sequence.py: 169 | - リモート用に,matplotlibのインポート箇所を変更 170 | 171 | 2019-04-27 Takuya Fukushima 172 | 173 | * calculation/calculate.py: 174 | * calculation/kick_distribution.py: 175 | * calculation/kick_sequence.py: 176 | * lib/lib_log_analyzer.py: 177 | - リモートホストでの実行に対応(デバッグモードは使用不可) 178 | 179 | 2019-02-08 Takuya Fukushima 180 | 181 | * calculation/calculate.py: 182 | * calculation/kick_distribution.py: 183 | * calculation/kick_sequence.py: 184 | - 図のフォント埋め込みを追加 185 | 186 | 2018-07-18 Takuya Fukushima 187 | 188 | * calculation/calcurate.py: 189 | * calculation/kick_distribution.py: 190 | * lib/lib_log_analyzer.py: 191 | - kick distributionの出力に対応 192 | 193 | 2018-07-18 Takuya Fukushima 194 | 195 | * calculation/kick_sequence.py: 196 | - ソートした敵プレイヤを全て出力するように変更 197 | 198 | * loop.sh 199 | - ループ時にインデックスが再度表示されるバグを修正 200 | 201 | 2018-07-18 Takuya Fukushima 202 | 203 | * loop.sh: 204 | - オプションを設定できないバグを修正 205 | 206 | 2018-07-17 Takuya Fukushima 207 | 208 | * calculation/calculate.py: 209 | - ボール支配率の計算をfloat型にキャスト 210 | 211 | 2018-07-17 Takuya Fukushima 212 | 213 | * loop.sh: 214 | - each-cycleのオプションをloopで用いることができるように変更 215 | 216 | 2018-07-16 Takuya Fukushima 217 | 218 | * calculation/kick_sequence.py: 219 | * lib/la_class.py: 220 | - 最近傍の敵を出力するようにoutputKickSequence()を変更. 221 | 222 | 2018-07-16 Takuya Fukushima 223 | 224 | * lib/lib_log_analyzer.py: 225 | - プレイヤリストをソートする関数を追加 226 | 227 | 2018-07-13 Takuya Fukushima 228 | 229 | * calculation/pass_check.py: 230 | * lib/lib_log_analyzer.py: 231 | - 座標の直接入力ではなくクラスを渡すように変更 232 | 233 | 2018-02-08 Takuya Fukushima 234 | 235 | * calculation/calcurate.py: 236 | * lib/la_class.py: 237 | * lib/lib_log_analyzer.py: 238 | * main.py: 239 | * calculation/kick_sequence.py: 240 | - ペナルティエリア侵入回数を追加 241 | 242 | * calculation/penalty_area.py: 243 | - 不要なファイルを削除 244 | 245 | 2018-01-05 Jiarun Zhong 246 | 247 | * calculation/dribble_count.py: 248 | * extraction/get_dash.py: 249 | - ドリブルの計算ファイルを追加 250 | 251 | * calculation/calculate.py: 252 | * extraction/get_dash.py: 253 | * lib/la_class.py: 254 | * main.py: 255 | - ドリブルを出力対象に追加 256 | 257 | 2017-12-29 Takuya Fukushima 258 | 259 | * loop.sh: 260 | * extraction/rcl_reader.py: 261 | * loop.py: 262 | * main.py: 263 | * lib/option.py: 264 | - ディレクトリループ用のシェルファイルを追加 265 | - loop.pyを削除 266 | 267 | 2017-12-01 Takuya Fukushima 268 | 269 | * calculation/possession.py: 270 | * calculation/win_lose.py: 271 | - 不要になったため削除 272 | 273 | * calculation/calculate.py: 274 | * calculation/pass_check.py: 275 | * calculation/shoot.py: 276 | * calculation/through_pass.py: 277 | * extraction/extract.py: 278 | * extraction/get_kick.py: 279 | * extraction/get_tackle.py: 280 | * extraction/referee.py: 281 | * lib/la_class.py: 282 | * lib/state.py: 283 | * main.py: 284 | - loganalyzer3用にファイルを整理 285 | - 細かいバグを修正 286 | 287 | 2017-11-27 Takuya Fukushima 288 | 289 | * calculation/calculate.py: 290 | * calculation/through_pass.py: 291 | * lib/la_class.py: 292 | * main.py: 293 | 294 | - through pass回数の実装 295 | - バグの修正 296 | 297 | 2017-11-27 Takuya Fukushima 298 | 299 | * calculation/offside_line.py: 300 | * calculation/pass_check.py: 301 | * calculation/shoot.py: 302 | * extraction/extract.py: 303 | * extraction/filename_split.py: 304 | * extraction/get_coordinate.py: 305 | * extraction/get_kick.py: 306 | * extraction/get_tackle.py: 307 | * extraction/hetero.py: 308 | * extraction/referee.py: 309 | * extraction/state.py: 310 | * lib/lib_log_analyzer.py: 311 | * lib/option.py: 312 | * main.py: 313 | - loganalyzer2を高速化 314 | - オプションの構造を変更 315 | 316 | * calculation/calculate.py: 317 | * extraction/rcg_reader.py: 318 | * extraction/rcl_reader.py: 319 | * lib/la_class.py: 320 | - classを用いた構造に変更 321 | 322 | 2017-10-24 An Ohori 323 | 324 | * ChangeLog: 325 | * main.py: 326 | * loop.py: 327 | 328 | - バグの修正 329 | 330 | 2017-10-24 An Ohori 331 | 332 | * ChangeLog: 333 | 334 | - 不要なファイルを削除して、それまでの変更を再び追加 335 | 336 | 2017-10-24 An Ohori 337 | 338 | * ChangeLog: 339 | * main.py: 340 | * loop.py: 341 | * calculation/distribution3dplot.py: 342 | 343 | - バグの修正 344 | 345 | 2017-10-24 An Ohori 346 | 347 | * ChangeLog: 348 | * main.py: 349 | * loop.py: 350 | 351 | - すべての項目を前後半それぞれ表示するように変更 352 | - 場所ごとのパス成功率を表示するように変更 353 | 354 | * calculation/distribution3dplot.py: 355 | * extraction/kick_txt.py: 356 | 357 | - キック分布を作成するファイルを追加 358 | 359 | * extraction/referee.py: 360 | - 前後半のイエローカード数を計算する関数を追加 361 | 362 | 2017-08-31 An Ohori 363 | 364 | * ChangeLog: 365 | * main.py: 366 | 367 | - ペナルティーエリアに入った回数と前後半の得点を表示するように変更 368 | 369 | * calculation/first_second.py: 370 | - 前後半の項目を計算するファイルを追加 371 | 372 | 2016-12-16 Takuya FUKUSHIMA 373 | 374 | * ChangeLog: 375 | * possession.py: 376 | 377 | - ボール支配率のバグを修正 378 | 379 | 380 | 2016-12-16 Takuya FUKUSHIMA 381 | 382 | * ChangeLog: 383 | * get_kick.py: 384 | 385 | - ゲーム終了後の行動を考慮しないように修正 386 | 387 | 388 | 2016-12-15 Takuya Fukushima 389 | 390 | * ChangeLog: 391 | * HowToUse: 392 | * extraction/get_tackle.py: 393 | * lib/option.py: 394 | * loop.py: 395 | 396 | - tackle のバグを修正 397 | - -gオプションの修正 398 | 399 | 400 | 2016-12-12 Sho Tanaka 401 | 402 | * ChangeLog: 403 | * HowToUse: 404 | 405 | * calculation/nearest_player.py: 406 | * calculation/penalty_area.py: 407 | 408 | - ボールに一番近いプレイヤを抽出するプログラムの追加 409 | - penalty_area.pyを元の状態に修復 410 | 411 | * lib/lib_log_analyzer.py 412 | - ファイルがloganalyzerのディレクトリ下になくとも対応可能なように変更 413 | 414 | * main.py 415 | - ファイルがloganalyzerのディレクトリ下になくとも対応可能なよう 416 | に変更 417 | 418 | * extraction/filename_split.py 419 | - ファイルがloganalyzerのディレクトリ下になくとも対応可能なよう 420 | に変更 421 | 422 | 423 | 2016-11-11 Takuya Fukushima 424 | 425 | * ChangeLog: 426 | 427 | * calculation/shoot.py: 428 | * extraction/extract.py: 429 | * lib/option.py: 430 | 431 | - 一部のオプションが無効になっていたため修正 432 | 433 | 434 | 2016-11-11 Takuya Fukushima 435 | 436 | * ChangeLog: 437 | * HowToUse: 438 | 439 | * *.pyc: 440 | - バイナリファイルの削除 441 | 442 | * extraction/extract.py: 443 | - ファイルオープン用のモジュールを追加 444 | 445 | * calculation/pass_check.py: 446 | * calculation/pass_probability.py: 447 | * calculation/penalty_area.py: 448 | * calculation/through_pass.py: 449 | * extraction/get_coordinate.py: 450 | * extraction/get_kick.py: 451 | * extraction/get_tackle.py: 452 | * extraction/hetero.py: 453 | * extraction/referee.py: 454 | * extraction/state.py: 455 | * loop.py: 456 | * main.py: 457 | - 繰り返しファイルオープンしないように修正 458 | - tackle と kick の16進数部分を削除 459 | 460 | 461 | 2016-10-31 Sho Tanaka 462 | 463 | * ChangeLog: 464 | * HowToUse: 465 | 466 | * calculation/through_pass.py: 467 | * calculation/pass_probability.py: 468 | * calculation/penalty_area.py: 469 | 470 | - スルーパスの回数を調べるファイルを追加 471 | - パス成功率を調べるファイルを追加 472 | - ペナルティエリア侵入回数を調べるファイルを追加 473 | - 上記でのタックルの判定を変更 474 | 475 | 2016-10-28 Takuya Fukushima 476 | 477 | * ChangeLog: 478 | * HowToUse: 479 | 480 | * extraction/get_kick.py: 481 | * extraction/get_kick.pyc: 482 | * lib/option.py: 483 | * lib/option.pyc: 484 | * main.py: 485 | 486 | * calculation/offside_line.py: 487 | * calculation/offside_line.pyc: 488 | * calculation/through_pass.py: 489 | * extraction/state.py: 490 | * extraction/state.pyc: 491 | * loop.py: 492 | * loop.pyc: 493 | 494 | - ディレクトリ内のファイルをループするように変更 495 | - スルーパスの回数を調べるためにファイルを追加(未完成) 496 | 497 | 2016-10-20 Takuya Fukushima 498 | 499 | * ChangeLog: 500 | * HowToUse: 501 | 502 | * extraction/referee.py: 503 | * extraction/referee.pyc: 504 | * lib/option.py: 505 | * lib/option.pyc: 506 | * main.py: 507 | 508 | - cycle,play_mode,位置情報を抽出するようにオプションを追加 509 | 510 | 2016-09-03 Takuya Fukushima 511 | 512 | * ChangeLog: 513 | 514 | * calculation/possession.py: 515 | * calculation/possession.pyc: 516 | * extraction/get_tackle.py: 517 | * extraction/get_tackle.pyc: 518 | * lib/lib_log_analyzer.py: 519 | * lib/lib_log_analyzer.pyc: 520 | 521 | - ボール支配率計算時のバグを修正 522 | 523 | 2016-09-02 Takuya Fukushima 524 | 525 | * calculation/pass_check.py: 526 | * calculation/pass_check.pyc: 527 | * calculation/possession.py: 528 | * calculation/possession.pyc: 529 | * extraction/get_coordinate.py: 530 | * extraction/get_coordinate.pyc: 531 | * lib/lib_log_analyzer.py: 532 | * lib/lib_log_analyzer.pyc: 533 | * main.py: 534 | 535 | * ChangeLog: 536 | 537 | - 同時キックが行われた場合にニュートラルにするよう変更 538 | 539 | 2016-08-24 Takuya Fukushima 540 | 541 | * main.py: 542 | 543 | * calculation/__init__.py: 544 | * calculation/__init__.pyc: 545 | * calculation/pass_check.py: 546 | * calculation/posession.py: 547 | * calculation/shoot.py: 548 | * calculation/win_lose.py: 549 | * extraction/__init__.py: 550 | * extraction/filename_split.py: 551 | * extraction/get_coordinate.py: 552 | * extraction/get_kick.py: 553 | * extraction/get_tackle.py: 554 | * extraction/hetero.py: 555 | * extraction/referee.py: 556 | * lib/__init__.py 557 | * lib/lib_log_analyzer.py: 558 | * lib/option.py: 559 | 560 | * ChangeLog: 561 | * HowToUse: 562 | 563 | - logの集計をcsv形式で出力するように設定 564 | -------------------------------------------------------------------------------- /lib/la_class.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # cython: language_level=3 3 | 4 | import os 5 | import csv 6 | import cython 7 | import itertools 8 | 9 | @cython.cclass 10 | class Position: 11 | x = cython.declare(cython.double, visibility='public') 12 | y = cython.declare(cython.double, visibility='public') 13 | def __init__(self) -> None: 14 | self.x = 0.0 15 | self.y = 0.0 16 | 17 | 18 | @cython.cclass 19 | class Ball: 20 | pos = cython.declare(Position, visibility='public') 21 | vel = cython.declare(Position, visibility='public') 22 | def __init__(self) -> None: 23 | self.pos = Position() 24 | self.vel = Position() 25 | 26 | 27 | @cython.cclass 28 | class Referee: 29 | playmode = cython.declare(str, visibility='public') 30 | say = cython.declare(list, visibility='public') 31 | said = cython.declare(cython.bint, visibility='public') 32 | def __init__(self) -> None: 33 | self.playmode = "" 34 | self.say = [] 35 | self.said = False 36 | 37 | 38 | @cython.cclass 39 | class HeteroParam: 40 | player_speed_max = cython.declare(cython.double, visibility='public') 41 | stamina_inc_max = cython.declare(cython.double, visibility='public') 42 | player_decay = cython.declare(cython.double, visibility='public') 43 | inertia_moment = cython.declare(cython.double, visibility='public') 44 | dash_power_rate = cython.declare(cython.double, visibility='public') 45 | player_size = cython.declare(cython.double, visibility='public') 46 | kickable_margin = cython.declare(cython.double, visibility='public') 47 | kick_land = cython.declare(cython.double, visibility='public') 48 | extra_stamina = cython.declare(cython.double, visibility='public') 49 | effort_max = cython.declare(cython.double, visibility='public') 50 | effort_min = cython.declare(cython.double, visibility='public') 51 | kick_power_rate = cython.declare(cython.double, visibility='public') 52 | foul_detect_probability = cython.declare(cython.double, visibility='public') 53 | catchable_area_l_stretch = cython.declare(cython.double, visibility='public') 54 | 55 | def __init__(self) -> None: 56 | self.player_speed_max = 1.05 57 | self.stamina_inc_max = 45.0 58 | self.player_decay = 0.4 59 | self.inertia_moment = 5.0 60 | self.dash_power_rate = 0.006 61 | self.player_size = 0.3 62 | self.kickable_margin = 0.7 63 | self.kick_land = 0.1 64 | self.extra_stamina = 50.0 65 | self.effort_max = 1.0 66 | self.effort_min = 0.6 67 | self.kick_power_rate = 0.027 68 | self.foul_detect_probability = 0.5 69 | self.catchable_area_l_stretch = 1.0 70 | 71 | 72 | @cython.cclass 73 | class ServerParam: 74 | pitch_length = cython.declare(cython.double, visibility='readonly') 75 | pitch_width = cython.declare(cython.double, visibility='readonly') 76 | goal_line_l = cython.declare(cython.double, visibility='readonly') 77 | goal_line_r = cython.declare(cython.double, visibility='readonly') 78 | goal_post = cython.declare(cython.int, visibility='readonly') 79 | penalty_area_x = cython.declare(cython.double, visibility='readonly') 80 | penalty_area_y = cython.declare(cython.double, visibility='readonly') 81 | ball_speed_decay = cython.declare(cython.double, visibility='readonly') 82 | hetero_params = cython.declare(list, visibility='public') 83 | 84 | def __init__(self) -> None: 85 | self.pitch_length = 105.0 86 | self.pitch_width = 68.0 87 | self.goal_line_l = -52.5 88 | self.goal_line_r = 52.5 89 | self.goal_post = 7 90 | self.penalty_area_x = 36.05 91 | self.penalty_area_y = 20.00 92 | self.ball_speed_decay = 0.94 93 | self.hetero_params = [] 94 | for i in range(18): 95 | self.hetero_params.append(HeteroParam()) 96 | 97 | 98 | @cython.cclass 99 | class Player: 100 | hetero = cython.declare(cython.int, visibility='public') 101 | state = cython.declare(str, visibility='public') 102 | pos = cython.declare(Position, visibility='public') 103 | vel = cython.declare(Position, visibility='public') 104 | body_angle = cython.declare(cython.double, visibility='public') 105 | neck_angle = cython.declare(cython.double, visibility='public') 106 | pointing_target = cython.declare(Position, visibility='public') 107 | quality = cython.declare(str, visibility='public') 108 | view_area = cython.declare(cython.int, visibility='public') 109 | stamina = cython.declare(cython.double, visibility='public') 110 | effort = cython.declare(cython.double, visibility='public') 111 | recovery = cython.declare(cython.double, visibility='public') 112 | stamina_capacity = cython.declare(cython.double, visibility='public') 113 | focus_target = cython.declare(str, visibility='public') 114 | kick_count = cython.declare(cython.int, visibility='public') 115 | dash_count = cython.declare(cython.int, visibility='public') 116 | turn_count = cython.declare(cython.int, visibility='public') 117 | catch_count = cython.declare(cython.int, visibility='public') 118 | move_count = cython.declare(cython.int, visibility='public') 119 | turn_neck_count = cython.declare(cython.int, visibility='public') 120 | change_view_count = cython.declare(cython.int, visibility='public') 121 | say_count = cython.declare(cython.int, visibility='public') 122 | tackle_count = cython.declare(cython.int, visibility='public') 123 | arm_target_count = cython.declare(cython.int, visibility='public') 124 | attention_count = cython.declare(cython.int, visibility='public') 125 | action = cython.declare(str, visibility='public') 126 | 127 | def __init__(self) -> None: 128 | self.hetero = -1 129 | self.state = "0x1" 130 | self.pos = Position() 131 | self.vel = Position() 132 | self.body_angle = 0.0 133 | self.neck_angle = 0.0 134 | self.pointing_target = Position() 135 | self.quality = "none" 136 | self.view_area = 0 137 | self.stamina = 8000.0 138 | self.effort = 0.0 139 | self.recovery = 0.0 140 | self.stamina_capacity = 130600.0 141 | self.focus_target = "none" 142 | self.kick_count = 0 143 | self.dash_count = 0 144 | self.turn_count = 0 145 | self.catch_count = 0 146 | self.move_count = 0 147 | self.turn_neck_count = 0 148 | self.change_view_count = 0 149 | self.say_count = 0 150 | self.tackle_count = 0 151 | self.arm_target_count = 0 152 | self.attention_count = 0 153 | self.action = "" 154 | 155 | 156 | @cython.cclass 157 | class Team: 158 | name = cython.declare(str, visibility='public') 159 | player = cython.declare(list, visibility='public') 160 | offsideLineX = cython.declare(cython.double, visibility='public') 161 | 162 | def __init__(self) -> None: 163 | self.name = "" 164 | self.player = [] 165 | for i in range(11): 166 | self.player.append(Player()) 167 | self.offsideLineX = 0.0 168 | 169 | 170 | @cython.cclass 171 | class WorldModel: 172 | ball = cython.declare(Ball, visibility='public') 173 | l = cython.declare(Team, visibility='public') 174 | r = cython.declare(Team, visibility='public') 175 | referee = cython.declare(Referee, visibility='public') 176 | dominate_side = cython.declare(str, visibility='public') 177 | last_kicker_unum = cython.declare(cython.int, visibility='public') 178 | last_kicked_cycle = cython.declare(cython.int, visibility='public') 179 | 180 | def __init__(self, team_l: str, team_r: str) -> None: 181 | self.ball = Ball() 182 | self.l = Team() 183 | self.l.name = team_l 184 | self.r = Team() 185 | self.r.name = team_r 186 | self.referee = Referee() 187 | self.dominate_side = "none" 188 | self.last_kicker_unum = -1 189 | self.last_kicked_cycle = -1 190 | 191 | def outputInfo(self, fname: str) -> None: 192 | mode: str = 'a' if os.path.exists(fname) else 'w' 193 | all_info: list = [ 194 | 'ball_pos_x', 195 | 'ball_pos_y', 196 | 'ball_vel_x', 197 | 'ball_vel_y' 198 | ] 199 | players_info: list = [ 200 | 'hetero_id', 201 | 'status', 202 | 'pos_x', 203 | 'pos_y', 204 | 'vel_x', 205 | 'vel_y', 206 | 'body_angle', 207 | 'neck_angle', 208 | 'pointing_x', 209 | 'pointing_y', 210 | 'quality', 211 | 'view_area', 212 | 'stamina', 213 | 'effort', 214 | 'recovery', 215 | 'stamina_capacity', 216 | 'focus_target', 217 | 'kick_count', 218 | 'dash_count', 219 | 'turn_count', 220 | 'catch_count', 221 | 'move_count', 222 | 'turn_neck_count', 223 | 'change_view_count', 224 | 'say_count', 225 | 'tackle_count', 226 | 'arm_target_count', 227 | 'attention_count', 228 | 'action' 229 | ] 230 | for s in ['l', 'r']: 231 | for unum in range(1, 12): 232 | for p in players_info: 233 | all_info.append('{}{}_{}'.format(s, unum, p)) 234 | 235 | with open(fname, mode) as f: 236 | csv_writer = csv.writer(f) 237 | 238 | # add column values 239 | if mode == 'w': 240 | csv_writer.writerow(all_info) 241 | 242 | row: list = [ 243 | self.ball.pos.x, 244 | self.ball.pos.y, 245 | self.ball.vel.x, 246 | self.ball.vel.y 247 | ] 248 | row.extend(list(itertools.chain.from_iterable( 249 | [[p.hetero, 250 | p.state, 251 | p.pos.x, 252 | p.pos.y, 253 | p.vel.x, 254 | p.vel.y, 255 | p.body_angle, 256 | p.neck_angle, 257 | p.pointing_target.x, 258 | p.pointing_target.y, 259 | p.quality, 260 | p.view_area, 261 | p.stamina, 262 | p.effort, 263 | p.recovery, 264 | p.stamina_capacity, 265 | p.focus_target, 266 | p.kick_count, 267 | p.dash_count, 268 | p.turn_count, 269 | p.catch_count, 270 | p.move_count, 271 | p.turn_neck_count, 272 | p.change_view_count, 273 | p.say_count, 274 | p.tackle_count, 275 | p.arm_target_count, 276 | p.attention_count, 277 | p.action] for p in self.l.player]))) 278 | row.extend(list(itertools.chain.from_iterable( 279 | [[p.hetero, 280 | p.state, 281 | p.pos.x, 282 | p.pos.y, 283 | p.vel.x, 284 | p.vel.y, 285 | p.body_angle, 286 | p.neck_angle, 287 | p.pointing_target.x, 288 | p.pointing_target.y, 289 | p.quality, 290 | p.view_area, 291 | p.stamina, 292 | p.effort, 293 | p.recovery, 294 | p.stamina_capacity, 295 | p.focus_target, 296 | p.kick_count, 297 | p.dash_count, 298 | p.turn_count, 299 | p.catch_count, 300 | p.move_count, 301 | p.turn_neck_count, 302 | p.change_view_count, 303 | p.say_count, 304 | p.tackle_count, 305 | p.arm_target_count, 306 | p.attention_count, 307 | p.action] for p in self.r.player]))) 308 | 309 | csv_writer.writerow(row) 310 | 311 | @cython.cclass 312 | class Feature: 313 | logname = cython.declare(str, visibility='public') 314 | target_team = cython.declare(str, visibility='public') 315 | index = cython.declare(list, visibility='public') 316 | 317 | kick_cycle = cython.declare(list, visibility='public') 318 | kick_path_x = cython.declare(list, visibility='public') 319 | kick_path_y = cython.declare(list, visibility='public') 320 | all_kick_path_x = cython.declare(list, visibility='public') 321 | all_kick_path_y = cython.declare(list, visibility='public') 322 | color4plt_ks = cython.declare(list, visibility='public') 323 | teammate_from_ball = cython.declare(list, visibility='public') 324 | opponent_from_ball = cython.declare(list, visibility='public') 325 | kicker = cython.declare(list, visibility='public') 326 | receiver = cython.declare(list, visibility='public') 327 | kick_sequence = cython.declare(list, visibility='public') 328 | 329 | team_point = cython.declare(list, visibility='public') 330 | date = cython.declare(str, visibility='public') 331 | final_result = cython.declare(cython.int, visibility='public') 332 | our_yellow = cython.declare(cython.int, visibility='public') 333 | opp_yellow = cython.declare(cython.int, visibility='public') 334 | our_tackle = cython.declare(cython.int, visibility='public') 335 | all_our_tackle = cython.declare(cython.int, visibility='public') 336 | opp_tackle = cython.declare(cython.int, visibility='public') 337 | all_opp_tackle = cython.declare(cython.int, visibility='public') 338 | our_shoot = cython.declare(cython.int, visibility='public') 339 | opp_shoot = cython.declare(cython.int, visibility='public') 340 | our_dominate_time = cython.declare(cython.int, visibility='public') 341 | opp_dominate_time = cython.declare(cython.int, visibility='public') 342 | our_possession = cython.declare(cython.double, visibility='public') 343 | opp_possession = cython.declare(cython.double, visibility='public') 344 | our_kick = cython.declare(list, visibility='public') 345 | opp_kick = cython.declare(list, visibility='public') 346 | our_pass = cython.declare(list, visibility='public') 347 | opp_pass = cython.declare(list, visibility='public') 348 | pass_propability = cython.declare(list, visibility='public') 349 | our_through_pass = cython.declare(cython.int, visibility='public') 350 | opp_through_pass = cython.declare(cython.int, visibility='public') 351 | penalty_area = cython.declare(cython.int, visibility='public') 352 | our_point = cython.declare(cython.int, visibility='public') 353 | opp_point = cython.declare(cython.int, visibility='public') 354 | our_dribble = cython.declare(cython.int, visibility='public') 355 | opp_dribble = cython.declare(cython.int, visibility='public') 356 | # our_dribble_dist = cython.declare(cython.int, visibility='public') 357 | # opp_dribble_dist = cython.declare(cython.int, visibility='public') 358 | our_penalty_area = cython.declare(cython.int, visibility='public') 359 | opp_penalty_area = cython.declare(cython.int, visibility='public') 360 | our_disconnected_player = cython.declare(cython.int, visibility='public') 361 | opp_disconnected_player = cython.declare(cython.int, visibility='public') 362 | 363 | 364 | def __init__(self) -> None: 365 | # team_point = [ the name of target team, the name of opponent team, target team's score, opponent team's score ] 366 | # kick = [ left, right, front, back ] 367 | # pass = [ left, right, front, back ] 368 | # pass_probability = [ all, penalty_area, attacking_third, middle_third, defensive_third ] 369 | 370 | self.logname = "" 371 | self.target_team = "none" 372 | 373 | self.index = ["date", 374 | "our_team", 375 | "opp_team", 376 | "our_final_team_point", 377 | "opp_final_team_point", 378 | "our_penalty_shootout_point", 379 | "opp_penalty_shootout_point", 380 | "final_result", # win 3, lose 0 draw 1 381 | "our_domination_time", 382 | "opp_domination_time", 383 | "our_possession", 384 | "opp_possession", 385 | "our_yellow", 386 | "opp_yellow", 387 | # "our_kick_all", 388 | # "our_kick_L", 389 | # "our_kick_R", 390 | # "our_kick_F", 391 | # "our_kick_B", 392 | # "opp_kick_all", 393 | # "opp_kick_L", 394 | # "opp_kick_R", 395 | # "opp_kick_F", 396 | # "opp_kick_B", 397 | "our_pass_all", 398 | "our_pass_L", 399 | "our_pass_R", 400 | "our_pass_F", 401 | "our_pass_B", 402 | "opp_pass_all", 403 | "opp_pass_L", 404 | "opp_pass_R", 405 | "opp_pass_F", 406 | "opp_pass_B", 407 | "our_through_pass", 408 | "opp_through_pass", 409 | "our_successed_tackle", 410 | "our_failed_tackle", 411 | "opp_successed_tackle", 412 | "opp_failed_tackle", 413 | "our_shoot", 414 | "opp_shoot", 415 | "our_point", 416 | "opp_point", 417 | "our_dribble", 418 | "opp_dribble", 419 | "our_penalty_area", 420 | "opp_penalty_area", 421 | "our_disconnected_player", 422 | "opp_disconnected_player"] 423 | 424 | # for calculate kick_sequence 425 | self.kick_cycle = [] 426 | self.kick_path_x = [] 427 | self.kick_path_y = [] 428 | self.all_kick_path_x = [] 429 | self.all_kick_path_y = [] 430 | self.color4plt_ks = [] 431 | self.teammate_from_ball = [] # sorted from ball 432 | self.opponent_from_ball = [] # sorted from ball 433 | self.kicker = [] 434 | self.receiver = [] 435 | self.kick_sequence = [] 436 | 437 | # for output 438 | self.team_point = ["0", "0", 0, 0, 0, 0] 439 | self.date = "" 440 | self.final_result = 0 441 | self.our_yellow = 0 442 | self.opp_yellow = 0 443 | self.our_tackle = 0 444 | self.all_our_tackle = 0 445 | self.opp_tackle = 0 446 | self.all_opp_tackle = 0 447 | self.our_shoot = 0 448 | self.opp_shoot = 0 449 | self.our_dominate_time = 0 450 | self.opp_dominate_time = 0 451 | self.our_possession = 0.0 452 | self.opp_possession = 0.0 453 | self.our_kick = [0, 0, 0, 0] 454 | self.opp_kick = [0, 0, 0, 0] 455 | self.our_pass = [0, 0, 0, 0] 456 | self.opp_pass = [0, 0, 0, 0] 457 | self.pass_propability = [] 458 | self.our_through_pass = 0 459 | self.opp_through_pass = 0 460 | self.penalty_area = 0 461 | self.our_point = 0 462 | self.opp_point = 0 463 | self.our_dribble = 0 464 | self.opp_dribble = 0 465 | # self.our_dribble_dist = 0 466 | # self.opp_dribble_dist = 0 467 | self.our_penalty_area = 0 468 | self.opp_penalty_area = -1 469 | self.our_disconnected_player = 0 470 | self.opp_disconnected_player = 0 471 | 472 | def outputIndexForIR(self, fname: str) -> None: 473 | mode: str = 'a' if os.path.exists(fname) else 'w' 474 | f = open(fname, mode) 475 | 476 | csvWriter = csv.writer(f) 477 | csvWriter.writerow(self.index) 478 | f.close() 479 | 480 | def outputIndexForR(self, fname: str) -> None: 481 | mode: cython.str = 'a' if os.path.exists(fname) else 'w' 482 | f = open(fname, mode) 483 | 484 | csvWriter = csv.writer(f) 485 | csvWriter.writerow(self.index) 486 | f.close() 487 | 488 | def outputIntegrateResult(self, fname: str) -> None: 489 | mode: cython.str = 'a' if os.path.exists(fname) else 'w' 490 | f = open(fname, mode) 491 | 492 | csvWriter = csv.writer(f) 493 | 494 | result: list = [self.date, 495 | self.team_point[0], 496 | self.team_point[1], 497 | self.team_point[2], 498 | self.team_point[3], 499 | self.team_point[4], 500 | self.team_point[5], 501 | self.final_result, 502 | self.our_dominate_time, 503 | self.opp_dominate_time, 504 | self.our_possession, 505 | self.opp_possession, 506 | self.our_yellow, 507 | self.opp_yellow, 508 | # sum( self.our_kick ), 509 | # self.our_kick[0], 510 | # self.our_kick[1], 511 | # self.our_kick[2], 512 | # self.our_kick[3], 513 | # sum( self.opp_kick ), 514 | # self.opp_kick[0], 515 | # self.opp_kick[1], 516 | # self.opp_kick[2], 517 | # self.opp_kick[3], 518 | sum(self.our_pass), 519 | self.our_pass[0], 520 | self.our_pass[1], 521 | self.our_pass[2], 522 | self.our_pass[3], 523 | sum(self.opp_pass), 524 | self.opp_pass[0], 525 | self.opp_pass[1], 526 | self.opp_pass[2], 527 | self.opp_pass[3], 528 | self.our_through_pass, 529 | self.opp_through_pass, 530 | self.our_tackle, 531 | self.all_our_tackle - self.our_tackle, 532 | self.opp_tackle, 533 | self.all_opp_tackle - self.opp_tackle, 534 | self.our_shoot, 535 | self.opp_shoot, 536 | self.our_point, 537 | self.opp_point, 538 | self.our_dribble, 539 | self.opp_dribble, 540 | self.our_penalty_area, 541 | self.opp_penalty_area, 542 | self.our_disconnected_player, 543 | self.opp_disconnected_player] 544 | 545 | csvWriter.writerow(result) 546 | f.close() 547 | 548 | def outputResult(self, fname) -> None: 549 | mode: cython.str = 'a' if os.path.exists(fname) else 'w' 550 | f = open(fname, mode) 551 | 552 | csvWriter = csv.writer(f) 553 | 554 | result: list = [self.date, 555 | self.team_point[0], 556 | self.team_point[1], 557 | self.team_point[2], 558 | self.team_point[3], 559 | self.team_point[4], 560 | self.team_point[5], 561 | self.final_result, 562 | self.our_dominate_time, 563 | self.opp_dominate_time, 564 | self.our_possession, 565 | self.opp_possession, 566 | self.our_yellow, 567 | self.opp_yellow, 568 | # sum( self.our_kick ), 569 | # self.our_kick[0], 570 | # self.our_kick[1], 571 | # self.our_kick[2], 572 | # self.our_kick[3], 573 | # sum( self.opp_kick ), 574 | # self.opp_kick[0], 575 | # self.opp_kick[1], 576 | # self.opp_kick[2], 577 | # self.opp_kick[3], 578 | sum(self.our_pass), 579 | self.our_pass[0], 580 | self.our_pass[1], 581 | self.our_pass[2], 582 | self.our_pass[3], 583 | sum(self.opp_pass), 584 | self.opp_pass[0], 585 | self.opp_pass[1], 586 | self.opp_pass[2], 587 | self.opp_pass[3], 588 | self.our_through_pass, 589 | self.opp_through_pass, 590 | self.our_tackle, 591 | self.all_our_tackle - self.our_tackle, 592 | self.opp_tackle, 593 | self.all_opp_tackle - self.opp_tackle, 594 | self.our_shoot, 595 | self.opp_shoot, 596 | self.our_point, 597 | self.opp_point, 598 | self.our_dribble, 599 | self.opp_dribble, 600 | self.our_penalty_area, 601 | self.opp_penalty_area, 602 | self.our_disconnected_player, 603 | self.opp_disconnected_player] 604 | 605 | csvWriter.writerow(result) 606 | f.close() 607 | --------------------------------------------------------------------------------