Global check on infiltrators

Discussion in 'Infiltrator' started by BloodyPuma, Apr 2, 2013.

  1. necrolamington

    If the cloak is unnecessary then how the hell is the infiltrator "OP" in your opinion?

    Every class gets access to SMG's and the cloak is really the only difference between the infiltrator and the engineer (besides hacking, but you said that you could match your HA's K/D without it) which is a non-combat oriented class, unlike the heavy assault.

    You can argue about the infiltrator all day but you cannot deny that we are an objectively weaker combat class and without the cloak (which has a confusion factor even though I don't believe that it is very good for avoiding non-oblivious enemies) and that the heavy with its objectively stronger armour and energy shield (not to mention rocket launcher) is stronger in direct combat (or indirect combat if provided with a flash radar).

    The only real way infiltrators could be considered OP would be if you were referring to sniping infiltrators which I agree are on par with if not better than other classes at killing infantry (although they have no anti armour capability and no real way to help with a push/objectives).
  2. Ztiller

    Actually, until you define "weaker", you can deny that. Because unless the only definition or "weaker" is "lower HP", it is not an objective statement, but rather an opinion.
  3. Vaphell

    goddamnit, i try to parametrize my pipeline but the data is full of holes, which make my scripts blow up in all kinds of ways.
    Info about banned haxxors(?) has next to no data and sometimes legit profiles happen to have a hole just because (maybe parser failure on my side?). It's hard to make an uniform approach because i have to plug in exception handling everywhere :mad:
    I am trying to figure out how to flatten the tree structure of data to a flat onedimensional format so it's easier to process in spreadsheets or whatever.
    • Up x 1
  4. K4is0r

    Even if you can handle the problems you mentioned, you still have the problem that you dont have a single statisticdata-table with which you can work with. If you want to use the top 100 killscore you are screwed because of the medics/engineers who get their xp through heals/revives/repairs and if you want to use the top 100 BRs you are screwed because some people use XP Boosts and some not.

    As i see it, a score like it is mentioned in the roadmap-thread http://forums.station.sony.com/ps2/?threads/82711 is the only way to get straight statistics...
  5. Vaphell

    yep, not having unmodified score/xp is dumb but at least in some cases it's not a problem. One can assume bonuses affect all classes of a given player equally so it doesn't change much if for example you want to calculate share of each class in the score.
    Data about score/kills/playtime on a per-class basis is available so it's possible to gouge relations between kills, score and playtime for each class of each player.
    I concentrated at score based leaderboards (though i can easily switch that), because if you pull enough records you get top killers either way. I think you can access first 10k records. According to the test samples i have (Connery), #3000 is ~BR40, #4000 ~BR35.
    I haven't touched vehicles and weapons (yet?) because the size of that data is ridiculous (and in unwieldy format on top of that). Once i pulled whole stats branch (stats = stats.class, stats.weapons, stats.vehicles) for single top100 just for kicks and got a file just below ~3 megs (!). Top1000 for all 9 servers would be ~250 megs (!!)


    my bash+python on linux pipeline that downloads and transforms data to an easy-to-import-into-spreadsheet format is pretty polished and mostly ready, but i'd love to test its customizability with different queries before sharing. Unfortunately leaderboards are down all the time and i have only one sample of data i downloaded 2 or 3 days ago.
  6. Vaphell

    My complete pipeline downloading and converting raw leaderboard data to spreadsheet ready format

    leaderboard.sh
    Code:
    #!/bin/bash
     
    declare -A servers
     
    # use provided .cfg or fall back to default.cfg
    (( $#!=0 )) && [ -f "$1" ] && cfg="$1"
    [ -f default.cfg ] && cfg=${cfg:-default.cfg}
    [ -n "$cfg" ] && source "$cfg"
     
    # tests, falling back on default values if cfg file is missing or incomplete
    mode=${mode:-Score}
    period=${period:-Forever}
    start=${start:-0}
    size=${size:-100}
    n=${n:-10}
    sleep=${sleep:-1}
    data_dir=${data_dir:-data_dump_$( date +%Y%m%d%H%M%S )}
    (( ${#servers[@]} )) || servers=( [1]=Connery [3]=Helios [9]=Woodman [10]=Miller [11]=Ceres [13]=Cobalt [17]=Mattherson [18]=Waterson [25]=Briggs )
    (( ${#char_data[@]} )) || char_data=( name.first experience.rank stats.{play_time,score,kills}.{value,class} )
     
    padding=$(( start+n*size ))  # fixed number width in file names to fit numbering scheme, eg 001-999=>3digits, 0001-9999=>4
    padding=${#padding}
     
     
     
    ##############################################################
     
    echo "* started: $( date )"
    echo "=[ Configuration ]="
    echo "  * mode: $mode"
    echo "  * period: $period"
    echo "  * start: $start"
    echo "  * size: $size"
    echo "  * n: $n"
    echo "  * interval: $sleep"
    echo "=[ Servers ]="
    for i in "${!servers[@]}"; do printf "  * %s (%d)\n" "${servers[$i]}" "$i"; done
    echo "=[ Data fields ]="
    printf "  * %s\n" "${char_data[@]}"
     
    resolve=$( printf "%s," "${char_data[@]}" )
    resolve='character('${resolve%,}')'
     
    for s in ${!servers[@]}
    do
      for(( i=start; i<n*size; i+=size ))
      do
        filter="name=${mode}&period=${period}&world=${s}&c:start=${i}&c:limit=${size}"
        url="https://census.soe.com/s:soe/get/ps2-beta/leaderboard/?${filter}&c:resolve=world(name.en)&c:resolve=${resolve}"
        mkdir -p "${data_dir}/${servers[$s]}"
        wget "$url" -O "${data_dir}/${servers[$s]}/$( printf "%0${padding}d-%0${padding}d.json" $((i+1)) $((i+size)) )"
        sleep "$sleep"
      done
    done
     
    echo "finished: $( date )"
    
    default.cfg
    Code:
    #=============================================================================
    #  example config file
    #=============================================================================
    #  cfg follows bash syntax (it's executed by the main script):
    #  - comments with '#'
    #  - assignments don't allow for gaps, eg 'start = 1' bad, 'start=1' good
    #  - {} allows to easy produce combinations, eg a{b,c{d,e}} => ab acd ace
    #
    #  about data fields:
    #  key pulls the whole subtree so usually it's best to be specific
    #  to cut down waste
    #  'stats' alone is generally a bad idea, so are eg stats.weapons,
    #  stats.vehicles because there are huge amounts of data under them, on
    #  the other hand 'name' is ok (only 2 fields)
     
     
    #=============================================================================
    #  settings
    #=============================================================================
     
    # start from record (records are indexed from 0)
    start=0
    # records in single request (max 100)
    size=100
    # requests per server,  n*size=X records (max 10k records?)
    n=1
    # leaderboards [Score/Kills/Time]
    mode=Score
    # leaderboards [Daily/Weekly/Monthly/Forever]
    period=Forever
    # interval between individual data requests (in seconds)
    sleep=1
    # dir where pulled data will be stored
    data_dir=./Score_top100
     
    # this cfg is a normal shell script, so one can do something like this:
    #    dir name in 'Score_YYMMDDhhmmss' format
    #    data_dir=./Score_$( date +%Y%m%d%H%M%S )
     
     
    #=============================================================================
    #  servers
    #=============================================================================
     
    # associative array ( [key]=value )
    servers=(
              [1]=Connery
              [3]=Helios
              [9]=Woodman
              [10]=Miller
              [11]=Ceres
              [13]=Cobalt
              [17]=Mattherson
              [18]=Waterson
              [25]=Briggs
            )
     
    #=============================================================================
    #  data fields
    #=============================================================================
     
    # array
    char_data=(
                name.first
                experience.rank
                stats.kill_death_ratio
                stats.{play_time,score,kills}.{value,class}
              )
     
    # available structures, {} is used to show branches
    # (not 100% complete list):
    #    name.{first,first_lower}
    #    experience.{rank,score}
    #    stats.{play_time,score,kills}.{value,class.{infiltrator,light_assault,heavy_assault,combat_medic,engineer,MAX}}
    #    stats.{play_time,score,kills}.vehicle  # big tree
    #    stats.{play_time,score,kills}.weapon  # huge tree
    #    stats.kill_death_ratio
    #    stats.assist_count
    #    stats.damage_given.{value,faction.{tr,vs,nc},vehicle,weapon}
    #    stats.damage_taken.{value,faction.{tr,vs,nc},vehicle,weapon}
    #    stats.deaths.{value,class,vehicle,weapon}
    #    stats.fire_count.{value,class,vehicle,weapon}
    #    stats.killed_by.{value,class,vehicle,weapon}
    #    stats.facility_capture_count.{value,faction.{tr,nc,vs}}
    #    stats.facility_defended_count
    #    stats.medal_count
    #    stats.revenge_count
    #    stats.vehicle_kills
    #    stats_daily      # mirrors structure of stats
    #    stats_weekly    # mirrors structure of stats
    #    stats_monthly    # mirrors structure of stats
    #    stats.one_life  # mirrors structure of stats
    #    certs.{currentpoints,percentagetonext}
    #    currency.{aerospace,infantry,mechanized,auraxium}
    #    times.{last_login,last_save,minutes_played}
    #    type.{creation_date,faction}
     
    #  there are also 'item_list', 'skill_list', 'loadouts' but they
    #  use lists not dictionaries. It's hard to make a coherent table out
    #  of these so they are skipped entirely during flattening
    
    flatten.py
    Code:
    #!/usr/bin/env python
     
    import sys
    import json
     
     
    class Record:
      def __init__( self, data, id ):
        self.data = data
        self.id = id
     
      def get_value( self, key ):
        global problems
        if key not in self.data or type( self.data[key] )==type( None ):
            problems += 1
            print >> sys.stderr, "warning: '{0}' key in record {1}".format( key, self.id )
            return ''
        else:
          return self.data[key]
     
      def print_row( self, fields, sep='|' ):
        print sep.join( self.get_value( x ) for x in fields )
     
      def print_rec( self, offset=0 ):
        print self.id
      #  for key in sorted( rec[0].items() ):
        for key in sorted( rec.data ):
          print '{0}{1}: {2}'.format( ' '*offset, key, self.get_value( key ) )
        print
     
     
    # flatten tree
    def flatten( s, sep='.', path='', flattened=None ):
      if flattened is None:
        flattened = {}
      for key, item in s.items():
        if isinstance( item, dict ):
          flatten( item, sep, path+key+sep, flattened )
        elif isinstance( item, list ):
          if len(item) < 2:
            flatten( item[0], sep, path+key+sep, flattened )
        else:
          flattened[path+key] = item
      return flattened
     
    # use elements of src to compile full list of fields just in case of inconsistencies
    def get_field_list( src ):
      temp = []
      for l in src:
        temp += [ x for x in flatten(l) ]
      temp = set( temp )
      return sorted( x for x in temp if '.' not in x )+sorted( x for x in temp if '.' in x )
     
     
    # header = compiled list of fields
    def print_header( fields, sep='|' ):
      print sep.join( fields )
     
    def param_check():
      sep = '|'
      verbose = False
      if len(sys.argv) > 1 and sys.argv[1] in ( '-h', '--help' ):
        print 'usage:'
        print '\t' + sys.argv[0] + ' --help'
        print '\t' + sys.argv[0] + ' <file(s)>'
        print '\t' + sys.argv[0] + ' -s <separator> <file(s)>'
        print '\t' + sys.argv[0] + ' -v <file(s)>'
        print 'options:'
        print '\t-h, --help\tthis help'
        print '\t-v\t\tverbose mode, records are printed in `key: value` form'
        print '\t-s\t\tseparator (-s <SEP>)'
        sys.exit(0)
      elif len(sys.argv) > 3 and sys.argv[1] == '-s':
        datafiles = sys.argv[3:]
        sep = sys.argv[2]
      elif len(sys.argv) > 2 and sys.argv[1] == '-v':
        datafiles = sys.argv[2:]
        verbose = True
      elif len(sys.argv) > 1:
        datafiles = sys.argv[1:]
      else:
        print 'Error! Bad list of arguments'
        print 'run {0} --help for details'.format( sys.argv[0] )
      return datafiles, sep, verbose
     
    def print_summary():
      print >> sys.stderr, '\nsummary:'
      print >> sys.stderr, '  records ignored due to errors: {0}/{1} ({2:.1}%)'.format( bad_records, g_index, 100.0*bad_records/g_index )
      print >> sys.stderr, '  problems encountered: {0}'.format( problems )
     
    #############################################################
     
    datafiles, sep, verbose = param_check()
    bad_records = 0
    problems = 0
    fields = []
    g_index = 0
    for df in datafiles:
      data = json.loads( open( df, 'r' ).read() )
      if len( fields ) == 0:
        fields = get_field_list( data['leaderboard_list'][0:5] )  # use first 5 rows (in case there are inconsinstencies) to establish full list of fields
        print_header( fields, sep )
      index = 0
      for char in data['leaderboard_list']:
        index += 1
        g_index += 1
        rec = Record( flatten(char), '#{0} ({1} #{2})'.format( g_index, df.split('.')[0], index ) )
        if len(rec.data) < max( 6, len(fields)/2 ):    # bad record if not enough data
          print >> sys.stderr, 'error: {0}/{1} fields in record {2}'.format( len(rec.data), len(fields), rec.id )
          bad_records += 1
          continue
        if verbose:
          rec.print_rec( 4 )
        else:
          rec.print_row( fields, sep )
    print_summary()
    Instructions:
    0. stop nubing around and boot linux
    0a. get virtualbox for windows and install it
    0b. create a virtual machine (10G should be plenty), and install latest ubuntu .iso in it (dl + installation 20min)
    0c. ???
    0d. profit!

    1. create workdir (eg 'planetside'), eg and put these 3 files there. Make .sh/.py executable via gui or via terminal
    Code:
    cd planetside
    chmod +x *.{sh,py}
    2. use default.cfg or create .cfg file(s) and tweak general settings, servers, data fields. Drop to terminal and use these configs with the shell script to download data, eg
    Code:
    ./leaderboard.sh default.cfg
    ./leaderboard.sh my_custom_config.cfg
    each server mentioned in .cfg gets its own subdir under configured destination dir
    names of datafiles are in '<start>-<end>.json' format (start indexed from 1, not 0 like in data)

    3. flatten data files with python script to spreadsheet friendly format, eg
    Code:
    ./flatten.py top100/*/*          # process all files in all subdirs of top100
    ./flatten.py top100/Connery/*    # process all files in top100/Connery
    ./flatten.py top100/*/0*        # process all files that match 0* (in all subdirs) 
    by default fields are separated with '|' but you can override it with -s, eg
    Code:
    ./flatten.py -s ',' ...
    verbose mode where records are printed out in human friendly form
    Code:
    ./flatten.py -v ...
    output is printed in terminal by default, to dump output to file use redirection >
    Code:
    ./flatten.py .... > output_file.csv
    4. open generated .csv in your spreadsheet of choice (libre office is installed in ubuntu by default)
    during import
    - select proper separator ('|' or whatever you used)
    - optional: make sure id/character_id are treated as text instead of number in scientific 9.999+E99 format (these fields are useless for stats either way)


    Demo of the whole procedure from download to spreadsheet-ready file using provided files:
    Code:
    ./leaderboard.sh default.cfg
    ./flatten.py Score_top100/*/* > score_top100.csv
    • Up x 1
  7. Chris Bingley

    Nope, I was referring to scout type infiltrators. I'll race my infiltrator against any HA to see who can deal with a phalanx turret quickest. Not only can an infiltrator hack a turret, this will also force the guy inside to spawn outside with about 1/2 second where they are redering outside, which is more than enough time to kill them. You also have a free turret you can use to harass the enemy.

    Infiltrators are weapon of mass distraction, whether they are scout type or sniper type. Scout types, by hacking terminals, assassinating lone enemies, placing proxy mines (or bouncing betties/claymores) hacking and using phalanx turrets etc force your enemies to come and find you. However, you are one person with an entire base (and the surrounding area) to hide in.

    If you want anti vehicle for you infilrator, use an enemy phalanx turret, or hack a vehicle terminal and spawn a MBT right inside there base.

    The options are there for the intelligent player who knows how to use the class. Learn to play or go back to CoD.
  8. Rohxer

    This tells me you haven't played an infiltrator since GU02. Or you're trolling. Or both and YOU need to L2P. It's way, way off-topic for this thread in any case. Troll elsewhere.
  9. Hoki

    Putting scout radar on cheap vehicles certainly doesn't help in that regard.

    Guys I'll go inf so that we can get some rada.. wait nm.

    Or

    Guys I'll go flank them with my gogo-gadget stealth.. wait they spawned a gocart with a 100m radius 5 minute duration radar, guess I won't be flanking ****.

    Oh and the comedy of cloak on low settings.
    If you can't do cloak right on low settings then infs should just be invisible on low settings. Why? Cause people are ******* doing it on purpose, punish them.
  10. GunsmithJoe

    Infil suit slot: Stealth (ignore radar)
    Universal Implant: EWR (radar detector)
    • Up x 2