Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc_api.py -> _build_subrequests needs more code #175

Open
Nostrademous opened this issue Aug 3, 2016 · 44 comments
Open

rpc_api.py -> _build_subrequests needs more code #175

Nostrademous opened this issue Aug 3, 2016 · 44 comments

Comments

@Nostrademous
Copy link

For AttackGym - we need to be able to send repeated BattleAction objects/classes. Currently rpc_api.py only handles "repeated" base-types (i.e., ints, longs, strings) by accepting a "list" and iterating over it and then appending to the necessary attribute. Need improvement to support AttackGym protobuf.

message AttackGymMessage {
    string gym_id = 1;
    string battle_id = 2;
    repeated .POGOProtos.Data.Battle.BattleAction attack_actions = 3;
    .POGOProtos.Data.Battle.BattleAction last_retrieved_actions = 4;
    double player_latitude = 5;
    double player_longitude = 6;
}

Something like: NOTE - COMPLETELY UNTESTED CODE (also, I'm using old protobuf file structure)

                for (key, value) in entry_content.items():
                    if isinstance(value, list):
                        #self.log.error('Found list: %s - trying as repeated', key)
                        for i in value:
                            #self.log.error('Value is %s' % (type(i)))
                            if isinstance(i, RpcSub.BattleAction):
                                self.log.error('RpcSub.BattleAction Class')
                                subsub_ext = get_class('pgoapi.protos.RpcSub_pb2.BattleAction')()
                                try:
                                    setattr(subsub_ext, 'Type', i.Type)
                                    setattr(subsub_ext, 'action_start_ms', i.action_start_ms)
                                    setattr(subsub_ext, 'duration_ms', i.duration_ms)
                                    setattr(subsub_ext, 'target_index', i.target_index) 
                                    r = getattr(subrequest_extension, key)
                                    r.append(subsub_ext)
                                except Exception as e:
                                    self.log.warning('Argument %s with value %s unknown inside %s (Exception: %s)', key, str(value), proto_name, str(e))

                            else:
                                try:
                                    r = getattr(subrequest_extension, key)
                                    r.append(i)
                                except Exception as e:
                                    self.log.warning('Arguement %s with value %s unknown inside %s (Exception %s)', key, i, proto_name, str(e))

There are probably much sexier python methods to do this

@Nihisil
Copy link

Nihisil commented Aug 4, 2016

I would like to have it implemented :)

@neotomek
Copy link

neotomek commented Aug 6, 2016

@Nostrademous can u past part of code where u call AttackGymMessage ?

@Nostrademous
Copy link
Author

@Nihisil @tejado Enjoy.

Let me know when Unknown6 is resolved and I will work on fixing any of the broken code... couldn't test it without solving the issue mentioned in this "issue" (which I can fix probably with code I suggested although @tejado would do it more eloquently probably) and then the Unknown6 issue happened.

@tejado - I moved the "decode_raw()" function to pgoapi/utilities.py so that I can invoke it when trouble shooting a specific API call by passing a debugflag through the "call()". Perhaps it's generally a good idea rather then keeping it in rpc_api.py exclusively.

    def investigateGym(self, fort, dist, direction):
        sGymColor = renum.TeamColor.DESCRIPTOR.values[fort.owned_by_team].name

        # get gym details
        gym_proto = self.getGymDetails(fort)
        if not gym_proto: return 0, 0, 0

        ##############################################
        # gather necessary details to start a battle #
        ##############################################

        # check if gym in battle
        if fort.is_in_battle:
            log.info('Gym currently in battle')
            return 0, 0, 0

        # check if gym is neutral/unowned - if so deploy to it
        if fort.owned_by_team == 0: # Neutral
            log.info('Gym @ [%f, %f] is NEUTRAL, %d meters %s' % (fort.latitude, fort.longitude, dist, direction))
            if dist < 32.0:
                self.deployPokemonToGym(fort.id)
                return 0, 0, 0
            else:
                return 1, fort.latitude, fort.longitude

        # save a boolean checking if we are on the same team
        sameTeam = self.my_team == fort.owned_by_team

        # find out who is defending
        defending_pokemon = []
        for defender in gym_proto.gym_state.memberships:
            defending_pokemon.append(defender.pokemon_data.id)

        # check gym prestige & level and membership
        gymLevel = self.getGymLevel(fort.gym_points)
        log.info('%s Gym @ [%f, %f] %d meters %s :: Prestige: %d, Level: %d, Pokemon: %d' % (sGymColor, fort.latitude, fort.longitude, dist, direction, fort.gym_points, gymLevel, len(defending_pokemon)))

        if sameTeam and gymLevel > len(defending_pokemon):
            log.info('We can deploy to gym @ [%f, %f] :: %d meters %s' % (fort.latitude, fort.longitude, dist, direction))
            if dist < 32.0:
                self.deployPokemonToGym(fort.id)
                return 0, 0, 0
            elif fort.id in self.gyms_with_my_pokemon:
                return 0, 0, 0
            else:
                return 1, fort.latitude, fort.longitude

        if not sameTeam:
            if dist < 32.0:
                self.battleGym(fort, sameTeam, defending_pokemon)
            else:
                log.info('Heading for gym to fight...')
                return 1, fort.latitude, fort.longitude
        return 0, 0, 0
    def battleGym(self, fort, sameTeam, defenders):
        # determine who we will attack with
        num = 6
        if sameTeam: num = 1
        my_attackers = self.pickAttackers(num) # this will be a list []

        # start the battle
        log.info('GymID: %s' % fort.id)
        log.info('Attacker list: %s' % str(my_attackers))
        log.info('Defender: %d' % defenders[0])
        self.api.start_gym_battle(gym_id=fort.id, attacking_pokemon_ids=my_attackers, defending_pokemon_id=defenders[0], player_latitude=self.p_lat, player_longitude=self.p_lng)

        start_reply = self.api.call(False, True)
        start_payload = self.getPayload(start_reply)
        if start_payload:
            start_resp = rsub.StartGymBattleResponse()
            start_resp.ParseFromString(start_payload)
            log.info(start_resp)

            if start_resp.result == 1: # SUCCESS
                tgt_indx = self.sendBlankAction(fort.id, start_resp.battle_id)
                if len(tgt_indx) > 0:
                    log.info('Starting Real Battle...')
                    battleState = start_resp.battle_log.state
                    serverTime = start_resp.battle_log.server_ms
                    while not self.battleConcluded(battleState):
                        log.info('Attacking...')
                        suc, battleState, serverTime, tgt_indx = self.sendAttack(fort.id, start_resp.battle_id, serverTime, tgt_indx[0], times=5)
                        if not suc:
                            log.error('something went wrong in our battle')
                            break
                    log.info('Battle Result: %d', battleState)
                    return 0
                else:
                    return 0
            else:
                return 0
        else:
            log.error('startGymBattle() request failed to get payload')
            log.error('Decode raw over protoc (protoc has to be in your PATH):\n\r%s', decode_raw(start_reply.content))
            return 0
    def sendAttack(self, gym_id, battle_id, serverTime, tgtIndx, times=5):
        actions = []
        for i in range(0, times):
            battleAction = rsub.BattleAction(Type=1, action_start_ms=(serverTime + 100*i), duration_ms=500, target_index=tgtIndx)
            actions.append(battleAction)
        self.api.attack_gym(gym_id=gym_id, battle_id=battle_id, attack_actions=actions, player_latitude=self.p_lat, player_longitude=self.p_lng)
        attack = self.api.call(False)
        attack_pay = self.getPayload(attack)
        if attack_pay:
            attack_resp = rsub.AttackGymResponse()
            attack_resp.ParseFromString(attack_pay)
            log.info(attack_resp)
            if attack_resp.result == 1: # SUCCESS
                target_index = []
                for action in attack_resp.battle_log.battle_actions:
                    log.info('Target Index: %d' % (action.target_index))
                    if action.target_index not in target_index: target_index.append(action.target_index)
                time.sleep(100*times)
                return 1, attack_resp.battle_log.state, attack_resp.battle_log.server_ms, target_index
            return 0, 0, 0, []
        else:
            log.error('sendAttack() failed to get payload')
            log.error('Decode raw over protoc (protoc has to be in your PATH):\n\r%s', decode_raw(attack.content))
        return 0, 0, 0, []


    def sendBlankAction(self, gym_id, battle_id):
        self.api.attack_gym(gym_id=gym_id, battle_id=battle_id, player_latitude=self.p_lat, player_longitude=self.p_lng)
        blank = self.api.call(False)
        blank_pay = self.getPayload(blank)
        if blank_pay:
            blank_resp = rsub.AttackGymResponse()
            blank_resp.ParseFromString(blank_pay)
            log.info(blank_resp)
            if blank_resp.result == 1: # SUCCESS
                target_index = []
                for action in blank_resp.battle_log.battle_actions:
                    log.info('Target Index: %d' % (action.target_index))
                    if action.target_index not in target_index: target_index.append(action.target_index)
                    return target_index
            return []
        else:
            log.error('sendBlankAction() failed to obtain response payload')
            return []


    def battleConcluded(self, battleState):
        if battleState in [2,3,4]: # 2 == VICTORY, 3 == DEFEATED, 4 == TIMED_OUT
            return True
        return False

@dnsBlah
Copy link

dnsBlah commented Aug 6, 2016

wow that looks awesome and really promissing
how'd you did this? Already before the last update now everything is down? Or... You alreay have it all working again? ;-)

Or is it untested yet?

Looks awesome! Nice work

@Nihisil
Copy link

Nihisil commented Aug 7, 2016

@Nostrademous Thank you

@elliottcarlson
Copy link
Contributor

Looks like this is resolved and can be closed.

@Nostrademous
Copy link
Author

@elliottcarlson how is this resolved? I posted theoretical code that I cannot test currently until I get the signatures working. Additionally, even if by some miracle the code I posted is 100% accurate it still needs to be accepted.

@elliottcarlson
Copy link
Contributor

You are correct, I lost track of the original ticket. Sorry about that.

On Aug 8, 2016 8:24 AM, "Nostrademous" [email protected] wrote:

@elliottcarlson https://github.com/elliottcarlson how is this resolved?
I posted theoretical code that I cannot test currently until I get the
signatures working. Additionally, even if by some miracle the code I posted
is 100% accurate it still needs to be accepted.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#175 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AANYSNp5YAzPXsQBsPr3v81FUO1TR0vCks5qdx_xgaJpZM4JcDux
.

@Nostrademous
Copy link
Author

Able to commence testing... however, of interest, I think I figured out a better way to deal with repeated request message parameters than what I suggested. We'll test soon.

@Nihisil
Copy link

Nihisil commented Aug 9, 2016

@Nostrademous thank you. I'm looking forward to see this better way. I wasn't able to make it work (I tried your suggestions and other different things)

@Nostrademous
Copy link
Author

@Nihisil so protobufs by design have an "add()" for any field labelled as repeated. That's my better way for dealing with lists that I want to try. At work currently so can't test for a bit.

@Nihisil
Copy link

Nihisil commented Aug 9, 2016

@Nostrademous yeah, I tried .add() and .extend([item]) but it didn't work for me, on these requests server return "responses": {"ATTACK_GYM": {"result": 2}}.

This is first time when I'm working with protobuf, so there is a big chance that I didn't get something essential.

I hope you will handle it in your free time :) good luck!

@Nostrademous
Copy link
Author

Getting ERROR_INVALID_ATTACK_ACTION reponses on my attacks... so glue is working, just need to fix the values I'm passing.

@Nihisil
Copy link

Nihisil commented Aug 10, 2016

@Nostrademous any news here? Can you, please, share your fix? So, I will try to make a correct attack action.

@Nostrademous
Copy link
Author

@Nihisil
#214

@Nostrademous
Copy link
Author

Got first wave of "attacking" working... to a result SUCCESS when submitting my attacks, my pokemon took some dmg, etc... but then when submitting next round of attacks got an ERROR_INVALID_ATTACK_ACTIONS.

So need some more tweaking.

@net8q
Copy link

net8q commented Aug 11, 2016

Tested PR here with a simple start battle and quit, seems to work

@HyperPeek
Copy link

Anyone succeeded in actually defeating a Pokemon in a battle? Using the fix #214 for the nested types works fine for sending the attack_actions in attack_gym and I see my generated actions appearing in ["responses"]["ATTACK_GYM"]["battle_log"].

However, despite tying various different settings the ["current_health"] of the attacked Pokemon never changed from its max value while mine lowered eventually leading to a faint (depending how long the battle lasts).

These are my findings so far:

  • Type, action_start_ms, duration_ms and target_index are mandatory in the BattleAction request
  • attacker_index or active_pokemon_id being set lead to an ERROR_INVALID_ATTACK_ACTIONS
  • target_pokemon_id, energy_delta, damage_windows_start_timestamp_mss or damage_windows_end_timestamp_mss can be set and appear in the battle_log but do not seem to do anything on the server side -- its primary use maybe for displaying combat actions in the app
  • timing of the action_start_ms in BattleAction is critical -- in reference to server_ms this should never be in the future (also not for the last member in the actions list) as otherwise the attack_gym request will result in ERROR_INVALID_ATTACK_ACTIONS
  • the last_retrieved_actions field in attack_gym controls the reply to the request -- without supplying that field the reply gets longer and longer as the battle_log contains the entire history. If a valid action is supplied in last_retrieved_actions, the reply from attack_gym contains only actions later than the one supplied

To me it appears, that the major goal of all this timing is to make the communication between app and server highly asynchronous with even several seconds lag still resulting in proper real-time feeling for the battle.

What I am not sure about is why the server accepts and displays the battle actions in its battle_log while they do not seem to have any effect on the battle, no matter what times etc. are supplied. Just ACTION_PLAYER_QUIT (Type = 7) seems to be interpreted correctly as it results in a defeat without further changes to "current_health" of the attacking pokemon. It looks like I am still missing something here.

If anyone has a log from a real battle it would probably help figuring out the missing bits -- tts been a while since I used MITM... .

@Nostrademous
Copy link
Author

Nostrademous commented Aug 17, 2016

I kill pokemon in gyms...

You have to send active_pokemon_id, damage_window_start_timestamp_mss, damage_window_end_timestamp_mss as well.

start timestamp is action_start_ms+duration_ms-200
end timestamp is action_start_ms+duration_ms

also yes, you have to set time for all these to be after last server_ms from battle_log, then queue about 1.5 seconds worth of fight via battle actions, then send the attack_gym after sleeping for 1.5 sec to make sure you are not in future.

make sure you set the 'last_retrieved_action' to the last action you saw in battle log (this tells the server where you are in the order of actions of the battle)

DODGING is a 500ms action.

@HyperPeek
Copy link

HyperPeek commented Aug 17, 2016

Ok - others I guess :-) So something is wrong with my timings? Or do you supply other fields than
Type, action_start_ms, duration_ms and target_index in BattleAction?
Did you try training against your own pokemon (to level the gym)? I found target_index to be -1 there always.

Do you use the damage_windows_start/end_timestamp ? And blank first attack is required, I guess (it is for me)?

NOTE: this all is explained in Nostrademous answer...

@Nostrademous
Copy link
Author

Nostrademous commented Aug 17, 2016

I answered in my response ... yes I set 3 other fields..

See code below... "dws" is my "dodge window" to dodge incoming attacks... a tuple of size 2 with (start, end) of dmg window

    def sendAttack(self, gym_id, battle_id, serverTime, last_action, dws, move_1, move_2):
        actions = []
        qm_dur = int(move_1['Sec.']*1000)
        qm_ed = int(move_1['Energy'])
        ch_dur = int(move_2['Sec.']*1000)
        ch_ed = int(move_2['Energy'])

        startTime = max(get_time(True), serverTime)
        battleTime = startTime 
        log.info('Current Time: %d, Last Server Time: %d' % (get_time(True), serverTime))
        # record responses for 1.5 sec worth of battle time
        while (battleTime - startTime) < 1500.0:
            if len(dws) == 0 or (battleTime + qm_dur + 65 + 100) < dws[0][0]:
                log.info('Quick Attack at %d (until %d)' % (battleTime, battleTime+qm_dur))
                battleAction = self.createQuickAttack(battleTime, qm_dur, qm_ed)
                battleTime += (qm_dur + 65)
            else:
                dodge_window = dws[0]
                timeDiff = (dodge_window[0] - 100) - battleTime
                log.info('Dodge at %d (until %d)' % (battleTime + timeDiff, battleTime+timeDiff+500))
                battleAction = self.createDodge(battleTime + timeDiff)
                battleTime += (timeDiff + 500 + 65)
                dws.remove(dodge_window)

            actions.append( protobuf_to_dict(battleAction) )

        lastAction = protobuf_to_dict(last_action)

        while get_time(True) < battleTime: time.sleep(0.1)
        #log.info('Current Time: %d' % (get_time(True)))

        attack = self.api.attack_gym(gym_id=gym_id, battle_id=battle_id, attack_actions=actions, last_retrieved_actions=lastAction, player_latitude=self.p_lat, player_longitude=self.p_lng)
        attack_pay = self.getPayload(attack)
        if attack_pay:
            attack_resp = rsub.AttackGymResponse()
            attack_resp.ParseFromString(attack_pay)
            if attack_resp.result == 1: # SUCCESS
                log.info('Current Time: %d, Last Server Time: %d' % (get_time(True), attack_resp.battle_log.server_ms))
                lastAction = None
                for action in attack_resp.battle_log.battle_actions:
                    lastAction = action
                return 1, attack_resp.battle_log.state, attack_resp.battle_log.server_ms, lastAction, attack_resp
            return 0, 0, 0, None, attack_resp
        else:
            log.error('sendAttack() failed to get payload')
        return 0, 0, 0, None, None
 def createQuickAttack(self, stime, move_duration, energy):
        return rsub.BattleAction(Type=1, action_start_ms=stime, duration_ms=move_duration, target_index=-1, active_pokemon_id=self.currAttacker, damage_windows_start_timestamp_mss=(stime+move_duration-200), damage_windows_end_timestamp_mss=(stime+move_duration))

    def createDodge(self, stime):
        return rsub.BattleAction(Type=2, action_start_ms=stime, duration_ms=500, target_index=-1, active_pokemon_id=self.currAttacker, damage_windows_start_timestamp_mss=(stime+500-200), damage_windows_end_timestamp_mss=(stime+500))

@HyperPeek
Copy link

Just got it basically working -- many thanks for the hints! Trick was getting the duration_ms right as they depend on the individual Pokemons. Any error in that results in ERROR_INVALID_ATTACK_ACTIONS and sometimes a new battle can not be started for some minutes...

If I would have more time it could be great fun to work on high-level strategies now -- or let the software learn and figure out the best moves to make or how to optimally take a gym :-)

@Nostrademous
Copy link
Author

@HyperPeek yeah, I made a JSON file for all move attacks for all pokemon...

@HyperPeek
Copy link

Taking data from monitoring them in fights...? ;)

@Nostrademous
Copy link
Author

no, databases are out there

@HyperPeek
Copy link

HyperPeek commented Aug 17, 2016

...getting off-topic -- issue can be closed, I guess -- but strange finding: if battling against own team Pokemon change after a few battles (~5) results in failure of start_gym_battle (empty response). Same after potion is used. Blackout time seems to be ~10 min. After that timespan all good again :)

update: the problem above can be avoided if the pokemon is battling until its fainted; in that case it can be "potioned" and start the next battle immediately... .

@DBa2016
Copy link

DBa2016 commented Aug 18, 2016

@Nostrademous : any chance to get this JSON file you are talking about?

@Nostrademous
Copy link
Author

Might not be 100% complete, but a good start.

{
    "13": {
        "Category": "Charge",
        "DPS": 6.3,
        "EPS": -5.0,
        "Energy": -20.0,
        "Move": "Wrap",
        "Power": 25.0,
        "Sec.": 4.0,
        "Type": "Normal"
    },
    "14": {
        "Category": "Charge",
        "DPS": 24.0,
        "EPS": -20.0,
        "Energy": -100.0,
        "Move": "Hyper Beam",
        "Power": 120.0,
        "Sec.": 5.0,
        "Type": "Normal"
    },
    "16": {
        "Category": "Charge",
        "DPS": 12.9,
        "EPS": -9.4,
        "Energy": -33.0,
        "Move": "Dark Pulse",
        "Power": 45.0,
        "Sec.": 3.5,
        "Type": "Dark"
    },
    "18": {
        "Category": "Charge",
        "DPS": 11.5,
        "EPS": -9.6,
        "Energy": -25.0,
        "Move": "Sludge",
        "Power": 30.0,
        "Sec.": 2.6,
        "Type": "Poison"
    },
    "20": {
        "Category": "Charge",
        "DPS": 11.9,
        "EPS": -9.5,
        "Energy": -20.0,
        "Move": "Vice Grip",
        "Power": 25.0,
        "Sec.": 2.1,
        "Type": "Normal"
    },
    "21": {
        "Category": "Charge",
        "DPS": 8.7,
        "EPS": -5.4,
        "Energy": -25.0,
        "Move": "Flame Wheel",
        "Power": 40.0,
        "Sec.": 4.6,
        "Type": "Fire"
    },
    "22": {
        "Category": "Charge",
        "DPS": 25.0,
        "EPS": -31.3,
        "Energy": -100.0,
        "Move": "Megahorn",
        "Power": 80.0,
        "Sec.": 3.2,
        "Type": "Bug"
    },
    "24": {
        "Category": "Charge",
        "DPS": 19.0,
        "EPS": -17.2,
        "Energy": -50.0,
        "Move": "Flamethrower",
        "Power": 55.0,
        "Sec.": 2.9,
        "Type": "Fire"
    },
    "26": {
        "Category": "Charge",
        "DPS": 12.1,
        "EPS": -5.7,
        "Energy": -33.0,
        "Move": "Dig",
        "Power": 70.0,
        "Sec.": 5.8,
        "Type": "Ground"
    },
    "28": {
        "Category": "Charge",
        "DPS": 30.0,
        "EPS": -50.0,
        "Energy": -100.0,
        "Move": "Cross Chop",
        "Power": 60.0,
        "Sec.": 2.0,
        "Type": "Fighting"
    },
    "30": {
        "Category": "Charge",
        "DPS": 10.5,
        "EPS": -6.6,
        "Energy": -25.0,
        "Move": "Psybeam",
        "Power": 40.0,
        "Sec.": 3.8,
        "Type": "Psychic"
    },
    "31": {
        "Category": "Charge",
        "DPS": 23.8,
        "EPS": -23.8,
        "Energy": -100.0,
        "Move": "Earthquake",
        "Power": 100.0,
        "Sec.": 4.2,
        "Type": "Ground"
    },
    "32": {
        "Category": "Charge",
        "DPS": 25.8,
        "EPS": -32.3,
        "Energy": -100.0,
        "Move": "Stone Edge",
        "Power": 80.0,
        "Sec.": 3.1,
        "Type": "Rock"
    },
    "33": {
        "Category": "Charge",
        "DPS": 12.9,
        "EPS": -9.4,
        "Energy": -33.0,
        "Move": "Ice Punch",
        "Power": 45.0,
        "Sec.": 3.5,
        "Type": "Ice"
    },
    "35": {
        "Category": "Charge",
        "DPS": 14.0,
        "EPS": -13.2,
        "Energy": -33.0,
        "Move": "Discharge",
        "Power": 35.0,
        "Sec.": 2.5,
        "Type": "Electric"
    },
    "36": {
        "Category": "Charge",
        "DPS": 15.4,
        "EPS": -8.5,
        "Energy": -33.0,
        "Move": "Flash Cannon",
        "Power": 60.0,
        "Sec.": 3.9,
        "Type": "Steel"
    },
    "38": {
        "Category": "Charge",
        "DPS": 14.8,
        "EPS": -12.2,
        "Energy": -33.0,
        "Move": "Drill Peck",
        "Power": 40.0,
        "Sec.": 2.7,
        "Type": "Flying"
    },
    "39": {
        "Category": "Charge",
        "DPS": 17.8,
        "EPS": -13.7,
        "Energy": -50.0,
        "Move": "Ice Beam",
        "Power": 65.0,
        "Sec.": 3.65,
        "Type": "Ice"
    },
    "40": {
        "Category": "Charge",
        "DPS": 25.6,
        "EPS": -25.6,
        "Energy": -100.0,
        "Move": "Blizzard",
        "Power": 100.0,
        "Sec.": 3.9,
        "Type": "Ice"
    },
    "42": {
        "Category": "Charge",
        "DPS": 21.1,
        "EPS": -26.3,
        "Energy": -100.0,
        "Move": "Heat Wave",
        "Power": 80.0,
        "Sec.": 3.8,
        "Type": "Fire"
    },
    "45": {
        "Category": "Charge",
        "DPS": 10.3,
        "EPS": -8.6,
        "Energy": -25.0,
        "Move": "Aerial Ace",
        "Power": 30.0,
        "Sec.": 2.9,
        "Type": "Flying"
    },
    "46": {
        "Category": "Charge",
        "DPS": 14.7,
        "EPS": -9.7,
        "Energy": -33.0,
        "Move": "Drill Run",
        "Power": 50.0,
        "Sec.": 3.4,
        "Type": "Ground"
    },
    "47": {
        "Category": "Charge",
        "DPS": 20.3,
        "EPS": -15.6,
        "Energy": -50.0,
        "Move": "Petal Blizzard",
        "Power": 65.0,
        "Sec.": 3.2,
        "Type": "Grass"
    },
    "49": {
        "Category": "Charge",
        "DPS": 17.6,
        "EPS": -11.8,
        "Energy": -50.0,
        "Move": "Bug Buzz",
        "Power": 75.0,
        "Sec.": 4.25,
        "Type": "Bug"
    },
    "50": {
        "Category": "Charge",
        "DPS": 10.4,
        "EPS": -8.3,
        "Energy": -20.0,
        "Move": "Poison Fang",
        "Power": 25.0,
        "Sec.": 2.4,
        "Type": "Poison"
    },
    "51": {
        "Category": "Charge",
        "DPS": 11.1,
        "EPS": -9.3,
        "Energy": -25.0,
        "Move": "Night Slash",
        "Power": 30.0,
        "Sec.": 2.7,
        "Type": "Dark"
    },
    "53": {
        "Category": "Charge",
        "DPS": 10.3,
        "EPS": -8.6,
        "Energy": -25.0,
        "Move": "Bubble Beam",
        "Power": 30.0,
        "Sec.": 2.9,
        "Type": "Water"
    },
    "54": {
        "Category": "Charge",
        "DPS": 14.3,
        "EPS": -15.7,
        "Energy": -33.0,
        "Move": "Submission",
        "Power": 30.0,
        "Sec.": 2.1,
        "Type": "Fighting"
    },
    "56": {
        "Category": "Charge",
        "DPS": 13.3,
        "EPS": -11.1,
        "Energy": -25.0,
        "Move": "Low Sweep",
        "Power": 30.0,
        "Sec.": 2.25,
        "Type": "Fighting"
    },
    "57": {
        "Category": "Charge",
        "DPS": 10.6,
        "EPS": -8.5,
        "Energy": -20.0,
        "Move": "Aqua Jet",
        "Power": 25.0,
        "Sec.": 2.35,
        "Type": "Water"
    },
    "58": {
        "Category": "Charge",
        "DPS": 19.1,
        "EPS": -21.3,
        "Energy": -50.0,
        "Move": "Aqua Tail",
        "Power": 45.0,
        "Sec.": 2.35,
        "Type": "Water"
    },
    "59": {
        "Category": "Charge",
        "DPS": 16.7,
        "EPS": -13.8,
        "Energy": -33.0,
        "Move": "Seed Bomb",
        "Power": 40.0,
        "Sec.": 2.4,
        "Type": "Grass"
    },
    "60": {
        "Category": "Charge",
        "DPS": 14.8,
        "EPS": -12.2,
        "Energy": -33.0,
        "Move": "Psyshock",
        "Power": 40.0,
        "Sec.": 2.7,
        "Type": "Psychic"
    },
    "62": {
        "Category": "Charge",
        "DPS": 9.7,
        "EPS": -6.9,
        "Energy": -25.0,
        "Move": "Ancient Power",
        "Power": 35.0,
        "Sec.": 3.6,
        "Type": "Rock"
    },
    "63": {
        "Category": "Charge",
        "DPS": 8.8,
        "EPS": -7.4,
        "Energy": -25.0,
        "Move": "Rock Tomb",
        "Power": 30.0,
        "Sec.": 3.4,
        "Type": "Rock"
    },
    "64": {
        "Category": "Charge",
        "DPS": 15.6,
        "EPS": -10.3,
        "Energy": -33.0,
        "Move": "Rock Slide",
        "Power": 50.0,
        "Sec.": 3.2,
        "Type": "Rock"
    },
    "65": {
        "Category": "Charge",
        "DPS": 13.8,
        "EPS": -11.4,
        "Energy": -33.0,
        "Move": "Power Gem",
        "Power": 40.0,
        "Sec.": 2.9,
        "Type": "Rock"
    },
    "69": {
        "Category": "Charge",
        "DPS": 9.7,
        "EPS": -8.1,
        "Energy": -25.0,
        "Move": "Ominous Wind",
        "Power": 30.0,
        "Sec.": 3.1,
        "Type": "Ghost"
    },
    "70": {
        "Category": "Charge",
        "DPS": 14.6,
        "EPS": -10.7,
        "Energy": -33.0,
        "Move": "Shadow Ball",
        "Power": 45.0,
        "Sec.": 3.08,
        "Type": "Ghost"
    },
    "72": {
        "Category": "Charge",
        "DPS": 10.7,
        "EPS": -8.9,
        "Energy": -25.0,
        "Move": "Magnet Bomb",
        "Power": 30.0,
        "Sec.": 2.8,
        "Type": "Steel"
    },
    "74": {
        "Category": "Charge",
        "DPS": 15.0,
        "EPS": -16.5,
        "Energy": -33.0,
        "Move": "Iron Head",
        "Power": 30.0,
        "Sec.": 2.0,
        "Type": "Steel"
    },
    "77": {
        "Category": "Charge",
        "DPS": 16.7,
        "EPS": -13.8,
        "Energy": -33.0,
        "Move": "Thunder Punch",
        "Power": 40.0,
        "Sec.": 2.4,
        "Type": "Electric"
    },
    "78": {
        "Category": "Charge",
        "DPS": 23.3,
        "EPS": -23.3,
        "Energy": -100.0,
        "Move": "Thunder",
        "Power": 100.0,
        "Sec.": 4.3,
        "Type": "Electric"
    },
    "79": {
        "Category": "Charge",
        "DPS": 20.4,
        "EPS": -18.5,
        "Energy": -50.0,
        "Move": "Thunderbolt",
        "Power": 55.0,
        "Sec.": 2.7,
        "Type": "Electric"
    },
    "80": {
        "Category": "Charge",
        "DPS": 9.3,
        "EPS": -7.4,
        "Energy": -20.0,
        "Move": "Twister",
        "Power": 25.0,
        "Sec.": 2.7,
        "Type": "Dragon"
    },
    "82": {
        "Category": "Charge",
        "DPS": 18.1,
        "EPS": -13.9,
        "Energy": -50.0,
        "Move": "Dragon Pulse",
        "Power": 65.0,
        "Sec.": 3.6,
        "Type": "Dragon"
    },
    "83": {
        "Category": "Charge",
        "DPS": 21.9,
        "EPS": -31.3,
        "Energy": -50.0,
        "Move": "Dragon Claw",
        "Power": 35.0,
        "Sec.": 1.6,
        "Type": "Dragon"
    },
    "84": {
        "Category": "Charge",
        "DPS": 6.4,
        "EPS": -5.1,
        "Energy": -20.0,
        "Move": "Disarming Voice",
        "Power": 25.0,
        "Sec.": 3.9,
        "Type": "Fairy"
    },
    "85": {
        "Category": "Charge",
        "DPS": 8.9,
        "EPS": -7.1,
        "Energy": -20.0,
        "Move": "Draining Kiss",
        "Power": 25.0,
        "Sec.": 2.8,
        "Type": "Fairy"
    },
    "86": {
        "Category": "Charge",
        "DPS": 13.1,
        "EPS": -7.9,
        "Energy": -33.0,
        "Move": "Dazzling Gleam",
        "Power": 55.0,
        "Sec.": 4.2,
        "Type": "Fairy"
    },
    "87": {
        "Category": "Charge",
        "DPS": 20.7,
        "EPS": -24.4,
        "Energy": -100.0,
        "Move": "Moonblast",
        "Power": 85.0,
        "Sec.": 4.1,
        "Type": "Fairy"
    },
    "88": {
        "Category": "Charge",
        "DPS": 19.0,
        "EPS": -17.2,
        "Energy": -50.0,
        "Move": "Play Rough",
        "Power": 55.0,
        "Sec.": 2.9,
        "Type": "Fairy"
    },
    "89": {
        "Category": "Charge",
        "DPS": 16.7,
        "EPS": -16.7,
        "Energy": -25.0,
        "Move": "Cross Poison",
        "Power": 25.0,
        "Sec.": 1.5,
        "Type": "Poison"
    },
    "90": {
        "Category": "Charge",
        "DPS": 21.2,
        "EPS": -19.2,
        "Energy": -50.0,
        "Move": "Sludge Bomb",
        "Power": 55.0,
        "Sec.": 2.6,
        "Type": "Poison"
    },
    "91": {
        "Category": "Charge",
        "DPS": 20.6,
        "EPS": -29.4,
        "Energy": -100.0,
        "Move": "Sludge Wave",
        "Power": 70.0,
        "Sec.": 3.4,
        "Type": "Poison"
    },
    "92": {
        "Category": "Charge",
        "DPS": 21.7,
        "EPS": -33.3,
        "Energy": -100.0,
        "Move": "Gunk Shot",
        "Power": 65.0,
        "Sec.": 3.0,
        "Type": "Poison"
    },
    "94": {
        "Category": "Charge",
        "DPS": 15.6,
        "EPS": -15.6,
        "Energy": -25.0,
        "Move": "Bone Club",
        "Power": 25.0,
        "Sec.": 1.6,
        "Type": "Ground"
    },
    "95": {
        "Category": "Charge",
        "DPS": 10.3,
        "EPS": -7.4,
        "Energy": -25.0,
        "Move": "Bulldoze",
        "Power": 35.0,
        "Sec.": 3.4,
        "Type": "Ground"
    },
    "96": {
        "Category": "Charge",
        "DPS": 11.5,
        "EPS": -9.6,
        "Energy": -25.0,
        "Move": "Mud Bomb",
        "Power": 30.0,
        "Sec.": 2.6,
        "Type": "Ground"
    },
    "99": {
        "Category": "Charge",
        "DPS": 14.5,
        "EPS": -10.6,
        "Energy": -33.0,
        "Move": "Signal Beam",
        "Power": 45.0,
        "Sec.": 3.1,
        "Type": "Bug"
    },
    "100": {
        "Category": "Charge",
        "DPS": 16.7,
        "EPS": -15.7,
        "Energy": -33.0,
        "Move": "X-Scissor",
        "Power": 35.0,
        "Sec.": 2.1,
        "Type": "Bug"
    },
    "101": {
        "Category": "Charge",
        "DPS": 8.1,
        "EPS": -6.5,
        "Energy": -20.0,
        "Move": "Flame Charge",
        "Power": 25.0,
        "Sec.": 3.1,
        "Type": "Fire"
    },
    "102": {
        "Category": "Charge",
        "DPS": 14.3,
        "EPS": -11.9,
        "Energy": -25.0,
        "Move": "Flame Burst",
        "Power": 30.0,
        "Sec.": 2.1,
        "Type": "Fire"
    },
    "103": {
        "Category": "Charge",
        "DPS": 24.4,
        "EPS": -24.4,
        "Energy": -100.0,
        "Move": "Fire Blast",
        "Power": 100.0,
        "Sec.": 4.1,
        "Type": "Fire"
    },
    "104": {
        "Category": "Charge",
        "DPS": 10.4,
        "EPS": -10.4,
        "Energy": -25.0,
        "Move": "Brine",
        "Power": 25.0,
        "Sec.": 2.4,
        "Type": "Water"
    },
    "105": {
        "Category": "Charge",
        "DPS": 10.6,
        "EPS": -7.6,
        "Energy": -25.0,
        "Move": "Water Pulse",
        "Power": 35.0,
        "Sec.": 3.3,
        "Type": "Water"
    },
    "106": {
        "Category": "Charge",
        "DPS": 13.8,
        "EPS": -8.3,
        "Energy": -33.0,
        "Move": "Scald",
        "Power": 55.0,
        "Sec.": 4.0,
        "Type": "Water"
    },
    "107": {
        "Category": "Charge",
        "DPS": 23.7,
        "EPS": -26.3,
        "Energy": -100.0,
        "Move": "Hydro Pump",
        "Power": 90.0,
        "Sec.": 3.8,
        "Type": "Water"
    },
    "108": {
        "Category": "Charge",
        "DPS": 19.6,
        "EPS": -17.9,
        "Energy": -50.0,
        "Move": "Psychic",
        "Power": 55.0,
        "Sec.": 2.8,
        "Type": "Psychic"
    },
    "111": {
        "Category": "Charge",
        "DPS": 6.6,
        "EPS": -5.3,
        "Energy": -20.0,
        "Move": "Icy Wind",
        "Power": 25.0,
        "Sec.": 3.8,
        "Type": "Ice"
    },
    "115": {
        "Category": "Charge",
        "DPS": 14.3,
        "EPS": -11.8,
        "Energy": -33.0,
        "Move": "Fire Punch",
        "Power": 40.0,
        "Sec.": 2.8,
        "Type": "Fire"
    },
    "116": {
        "Category": "Charge",
        "DPS": 24.5,
        "EPS": -20.4,
        "Energy": -100.0,
        "Move": "Solar Beam",
        "Power": 120.0,
        "Sec.": 4.9,
        "Type": "Grass"
    },
    "117": {
        "Category": "Charge",
        "DPS": 19.6,
        "EPS": -17.9,
        "Energy": -50.0,
        "Move": "Leaf Blade",
        "Power": 55.0,
        "Sec.": 2.8,
        "Type": "Grass"
    },
    "118": {
        "Category": "Charge",
        "DPS": 25.0,
        "EPS": -35.7,
        "Energy": -100.0,
        "Move": "Power Whip",
        "Power": 70.0,
        "Sec.": 2.8,
        "Type": "Grass"
    },
    "121": {
        "Category": "Charge",
        "DPS": 9.1,
        "EPS": -7.6,
        "Energy": -25.0,
        "Move": "Air Cutter",
        "Power": 30.0,
        "Sec.": 3.3,
        "Type": "Flying"
    },
    "122": {
        "Category": "Charge",
        "DPS": 25.0,
        "EPS": -31.3,
        "Energy": -100.0,
        "Move": "Hurricane",
        "Power": 80.0,
        "Sec.": 3.2,
        "Type": "Flying"
    },
    "123": {
        "Category": "Charge",
        "DPS": 18.8,
        "EPS": -20.6,
        "Energy": -33.0,
        "Move": "Brick Break",
        "Power": 30.0,
        "Sec.": 1.6,
        "Type": "Fighting"
    },
    "125": {
        "Category": "Charge",
        "DPS": 10.0,
        "EPS": -8.3,
        "Energy": -25.0,
        "Move": "Swift",
        "Power": 30.0,
        "Sec.": 3.0,
        "Type": "Normal"
    },
    "126": {
        "Category": "Charge",
        "DPS": 11.4,
        "EPS": -11.4,
        "Energy": -25.0,
        "Move": "Horn Attack",
        "Power": 25.0,
        "Sec.": 2.2,
        "Type": "Normal"
    },
    "127": {
        "Category": "Charge",
        "DPS": 14.3,
        "EPS": -11.9,
        "Energy": -25.0,
        "Move": "Stomp",
        "Power": 30.0,
        "Sec.": 2.1,
        "Type": "Normal"
    },
    "129": {
        "Category": "Charge",
        "DPS": 16.7,
        "EPS": -15.7,
        "Energy": -33.0,
        "Move": "Hyper Fang",
        "Power": 35.0,
        "Sec.": 2.1,
        "Type": "Normal"
    },
    "131": {
        "Category": "Charge",
        "DPS": 25.6,
        "EPS": -32.1,
        "Energy": -50.0,
        "Move": "Body Slam",
        "Power": 40.0,
        "Sec.": 1.56,
        "Type": "Normal"
    },
    "133": {
        "Category": "Charge",
        "DPS": 8.8,
        "EPS": -11.8,
        "Energy": -20.0,
        "Move": "Struggle",
        "Power": 15.0,
        "Sec.": 1.7,
        "Type": "Normal"
    },
    "200": {
        "Category": "Fast",
        "DPS": 7.5,
        "EPS": 30.0,
        "Energy": 12.0,
        "Move": "Fury Cutter",
        "Power": 3.0,
        "Sec.": 0.4,
        "Type": "Bug"
    },
    "201": {
        "Category": "Fast",
        "DPS": 11.1,
        "EPS": 15.6,
        "Energy": 7.0,
        "Move": "Bug Bite",
        "Power": 5.0,
        "Sec.": 0.45,
        "Type": "Bug"
    },
    "202": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Bite",
        "Power": 6.0,
        "Sec.": 0.5,
        "Type": "Dark"
    },
    "203": {
        "Category": "Fast",
        "DPS": 10.0,
        "EPS": 5.7,
        "Energy": 4.0,
        "Move": "Sucker Punch",
        "Power": 7.0,
        "Sec.": 0.7,
        "Type": "Dark"
    },
    "204": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Dragon Breath",
        "Power": 6.0,
        "Sec.": 0.5,
        "Type": "Dragon"
    },
    "205": {
        "Category": "Fast",
        "DPS": 8.3,
        "EPS": 11.7,
        "Energy": 7.0,
        "Move": "Thunder Shock",
        "Power": 5.0,
        "Sec.": 0.6,
        "Type": "Electric"
    },
    "206": {
        "Category": "Fast",
        "DPS": 10.0,
        "EPS": 5.7,
        "Energy": 4.0,
        "Move": "Spark",
        "Power": 7.0,
        "Sec.": 0.7,
        "Type": "Electric"
    },
    "207": {
        "Category": "Fast",
        "DPS": 8.3,
        "EPS": 11.7,
        "Energy": 7.0,
        "Move": "Low Kick",
        "Power": 5.0,
        "Sec.": 0.6,
        "Type": "Fighting"
    },
    "208": {
        "Category": "Fast",
        "DPS": 7.5,
        "EPS": 8.8,
        "Energy": 7.0,
        "Move": "Karate Chop",
        "Power": 6.0,
        "Sec.": 0.8,
        "Type": "Fighting"
    },
    "209": {
        "Category": "Fast",
        "DPS": 9.5,
        "EPS": 6.7,
        "Energy": 7.0,
        "Move": "Ember",
        "Power": 10.0,
        "Sec.": 1.05,
        "Type": "Fire"
    },
    "210": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 9.3,
        "Energy": 7.0,
        "Move": "Wing Attack",
        "Power": 9.0,
        "Sec.": 0.75,
        "Type": "Flying"
    },
    "211": {
        "Category": "Fast",
        "DPS": 8.7,
        "EPS": 8.7,
        "Energy": 10.0,
        "Move": "Peck",
        "Power": 10.0,
        "Sec.": 1.15,
        "Type": "Flying"
    },
    "212": {
        "Category": "Fast",
        "DPS": 10.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Lick",
        "Power": 5.0,
        "Sec.": 0.5,
        "Type": "Ghost"
    },
    "213": {
        "Category": "Fast",
        "DPS": 11.6,
        "EPS": 7.4,
        "Energy": 7.0,
        "Move": "Shadow Claw",
        "Power": 11.0,
        "Sec.": 0.95,
        "Type": "Ghost"
    },
    "214": {
        "Category": "Fast",
        "DPS": 10.8,
        "EPS": 10.8,
        "Energy": 7.0,
        "Move": "Vine Whip",
        "Power": 7.0,
        "Sec.": 0.65,
        "Type": "Grass"
    },
    "215": {
        "Category": "Fast",
        "DPS": 10.3,
        "EPS": 4.8,
        "Energy": 7.0,
        "Move": "Razor Leaf",
        "Power": 15.0,
        "Sec.": 1.45,
        "Type": "Grass"
    },
    "216": {
        "Category": "Fast",
        "DPS": 10.9,
        "EPS": 12.7,
        "Energy": 7.0,
        "Move": "Mud Shot",
        "Power": 6.0,
        "Sec.": 0.55,
        "Type": "Ground"
    },
    "217": {
        "Category": "Fast",
        "DPS": 10.7,
        "EPS": 5.0,
        "Energy": 7.0,
        "Move": "Ice Shard",
        "Power": 15.0,
        "Sec.": 1.4,
        "Type": "Ice"
    },
    "218": {
        "Category": "Fast",
        "DPS": 11.1,
        "EPS": 8.6,
        "Energy": 7.0,
        "Move": "Frost Breath",
        "Power": 9.0,
        "Sec.": 0.81,
        "Type": "Ice"
    },
    "219": {
        "Category": "Fast",
        "DPS": 7.5,
        "EPS": 5.3,
        "Energy": 7.0,
        "Move": "Quick Attack",
        "Power": 10.0,
        "Sec.": 1.33,
        "Type": "Normal"
    },
    "220": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Scratch",
        "Power": 6.0,
        "Sec.": 0.5,
        "Type": "Normal"
    },
    "221": {
        "Category": "Fast",
        "DPS": 10.9,
        "EPS": 6.4,
        "Energy": 7.0,
        "Move": "Tackle",
        "Power": 12.0,
        "Sec.": 1.1,
        "Type": "Normal"
    },
    "222": {
        "Category": "Fast",
        "DPS": 13.0,
        "EPS": 13.0,
        "Energy": 7.0,
        "Move": "Pound",
        "Power": 7.0,
        "Sec.": 0.54,
        "Type": "Normal"
    },
    "224": {
        "Category": "Fast",
        "DPS": 11.4,
        "EPS": 6.7,
        "Energy": 7.0,
        "Move": "Poison Jab",
        "Power": 12.0,
        "Sec.": 1.05,
        "Type": "Poison"
    },
    "225": {
        "Category": "Fast",
        "DPS": 9.5,
        "EPS": 6.7,
        "Energy": 7.0,
        "Move": "Acid",
        "Power": 10.0,
        "Sec.": 1.05,
        "Type": "Poison"
    },
    "226": {
        "Category": "Fast",
        "DPS": 12.3,
        "EPS": 12.3,
        "Energy": 7.0,
        "Move": "Psycho Cut",
        "Power": 7.0,
        "Sec.": 0.57,
        "Type": "Psychic"
    },
    "227": {
        "Category": "Fast",
        "DPS": 8.8,
        "EPS": 5.1,
        "Energy": 7.0,
        "Move": "Rock Throw",
        "Power": 12.0,
        "Sec.": 1.36,
        "Type": "Rock"
    },
    "228": {
        "Category": "Fast",
        "DPS": 12.7,
        "EPS": 11.1,
        "Energy": 7.0,
        "Move": "Metal Claw",
        "Power": 8.0,
        "Sec.": 0.63,
        "Type": "Steel"
    },
    "229": {
        "Category": "Fast",
        "DPS": 8.3,
        "EPS": 5.8,
        "Energy": 7.0,
        "Move": "Bullet Punch",
        "Power": 10.0,
        "Sec.": 1.2,
        "Type": "Steel"
    },
    "230": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Water Gun",
        "Power": 6.0,
        "Sec.": 0.5,
        "Type": "Water"
    },
    "231": {
        "Category": "Fast",
        "DPS": 0.0,
        "EPS": 5.7,
        "Energy": 7.0,
        "Move": "Splash",
        "Power": 0.0,
        "Sec.": 1.23,
        "Type": "Water"
    },
    "232": {
        "Category": "Fast",
        "DPS": 12.0,
        "EPS": 14.0,
        "Energy": 7.0,
        "Move": "Water Gun",
        "Power": 6.0,
        "Sec.": 0.5,
        "Type": "Water"
    },
    "234": {
        "Category": "Fast",
        "DPS": 11.4,
        "EPS": 3.8,
        "Energy": 4.0,
        "Move": "Zen Headbutt",
        "Power": 12.0,
        "Sec.": 1.05,
        "Type": "Psychic"
    },
    "235": {
        "Category": "Fast",
        "DPS": 9.9,
        "EPS": 4.6,
        "Energy": 7.0,
        "Move": "Confusion",
        "Power": 15.0,
        "Sec.": 1.51,
        "Type": "Psychic"
    },
    "236": {
        "Category": "Fast",
        "DPS": 10.3,
        "EPS": 6.9,
        "Energy": 4.0,
        "Move": "Poison Sting",
        "Power": 6.0,
        "Sec.": 0.58,
        "Type": "Poison"
    },
    "237": {
        "Category": "Fast",
        "DPS": 10.9,
        "EPS": 6.5,
        "Energy": 15.0,
        "Move": "Bubble",
        "Power": 25.0,
        "Sec.": 2.3,
        "Type": "Water"
    },
    "238": {
        "Category": "Fast",
        "DPS": 11.5,
        "EPS": 6.7,
        "Energy": 7.0,
        "Move": "Feint Attack",
        "Power": 12.0,
        "Sec.": 1.04,
        "Type": "Dark"
    },
    "239": {
        "Category": "Fast",
        "DPS": 11.3,
        "EPS": 3.0,
        "Energy": 4.0,
        "Move": "Steel Wing",
        "Power": 15.0,
        "Sec.": 1.33,
        "Type": "Steel"
    },
    "240": {
        "Category": "Fast",
        "DPS": 11.9,
        "EPS": 4.8,
        "Energy": 4.0,
        "Move": "Fire Fang",
        "Power": 10.0,
        "Sec.": 0.84,
        "Type": "Fire"
    },
    "241": {
        "Category": "Fast",
        "DPS": 10.6,
        "EPS": 5.0,
        "Energy": 7.0,
        "Move": "Rock Smash",
        "Power": 15.0,
        "Sec.": 1.41,
        "Type": "Fighting"
    }
}

@Nostrademous
Copy link
Author

You can then use info here: https://www.vg247.com/2016/07/27/pokemon-go-battle-type-strengths-and-weaknesses-explained/
To pick appropriate pokemon counters to gym defenders so you get a farther edge (based on "Type") of attack and Type of your Pokemon

@DBa2016
Copy link

DBa2016 commented Aug 18, 2016

Many thanks, this helps. I am still struggling to setup AttackGym request correctly. Most of the times, I receive "status_code: 3" back, but sometimes I get an ATTACK_SUCCESS and my head pokemon dies.

@domeops
Copy link

domeops commented Aug 18, 2016

Hey DBa2016, look at this : http://pastebin.com/enDzFvUN

That is a dump from someone playing the game live, so those packets are exactly how the structure should be handled. Granted you have to alter things based on pokemon etc and the ms variables are particularly finicky.

I still haven't implemented the fix provided yet myself but I am able to successfully send packets and get responses, my actions just don't play out properly.

@Nostrademous
Copy link
Author

@domeops that's from a pre 0.33 patch. It's not exactly correct. Code I pasted above works.

@DBa2016
Copy link

DBa2016 commented Aug 19, 2016

Tried to implement it in C#... Does not exactly work. I get status_code 3 for around 50% of requests...

@DBa2016
Copy link

DBa2016 commented Aug 19, 2016

Okay, playing around with timings etc. did the trick.. I still get "invalid actions" every now and then, but it's better now.

@DBa2016
Copy link

DBa2016 commented Aug 19, 2016

Hmmm... About every 3rd AttackGym request results in "invalid actions", but a recreated request works then...

Also - when one defending pokemon is killed, the State field switches to Victory, even though more pokemons are waiting to get a beating. Any idea how to get past this?

@firebolt55439
Copy link

@DBa2016 IIRC, you start a new gym battle with the next defending pokemon's ID.

On Aug 19, 2016, at 1:46 PM, DBa2016 [email protected] wrote:

Hmmm... About every 3rd AttackGym request results in "invalid actions", but a recreated request works then...

Also - when one defending pokemon is killed, the State field switches to Victory, even though more pokemons are waiting to get a beating. Any idea how to get past this?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@DBa2016
Copy link

DBa2016 commented Aug 20, 2016

@firebolt55439 : tried it, I get an "UNSET" response when I tryy to call StartGymBattle in that state...

@firebolt55439
Copy link

firebolt55439 commented Aug 21, 2016

@DBa2016 Make sure you sent a last BattleAction with the last_retrieved_action set to the ACTION_VICTORY action. Then, start the battle.

@DBa2016
Copy link

DBa2016 commented Aug 21, 2016

@firebolt55439 "StartGymBattle" does not have any last battle ction as a parameter.

@firebolt55439
Copy link

@DBa2016 I mean you have to send a last AttackGym request w/ the last
battle action set to the ACTION_VICTORY, then wait 1-2 seconds (simulate
cooldown) and send the StartGymBattle request with the next defending
pokemon ID (I suggest you store all the defending pokemon's at the
beginning of the first battle).

On Sun, Aug 21, 2016 at 1:40 AM, DBa2016 [email protected] wrote:

@firebolt55439 https://github.com/firebolt55439 "StartGymBattle" does
not have any last battle ction as a parameter.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#175 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABO7p2fJSKehAVByR3K_WG1BaMUy5cLkks5qiA8MgaJpZM4JcDux
.

-Sumer

@DBa2016
Copy link

DBa2016 commented Aug 22, 2016

@firebolt55439 - okay, will try this later, thanks...

By the way: I saw the action with the ACTION_VICTORY has "BattleData" element, with in turn a "nextDefenderPokemonId" element. However, this is signed long and sometimes negative, I did not find out when it is really negative. Might be useful.

@firebolt55439
Copy link

@DBa2016 that nextdefenderpokemonid element is rarely correct (something to do with fixed64/int64 or signedness) - just save the defender ID's at the beginning

On Aug 21, 2016, at 11:04 PM, DBa2016 [email protected] wrote:

@firebolt55439 - okay, will try this later, thanks...

By the way: I saw the action with the ACTION_VICTORY has "BattleData" element, with in turn a "nextDefenderPokemonId" element. However, this is signed long and sometimes negative, I did not find out when it is really negative. Might be useful.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@ConstiZ
Copy link

ConstiZ commented Sep 17, 2016

I know nobody has written on here in a long time, but I don't know where else to post (if somebody could hint me to a forum or something...)
I've been unsuccessfully at it for 4 days now, so some help would be nice.

I'm trying to get a battle to work using the code, but I can't seem to execute a move.
Either my pokemon gets defeated or it times out.

When sending an 'ATTACK_GYM' request, I get following warning for the BattleAction and last_action:

2016-09-17 20:14:29,128 [    pgoapi] [ INFO] Creating a new direct request...
2016-09-17 20:14:29,128 [    pgoapi] [ INFO] Adding 'ATTACK_GYM' to RPC request including arguments
2016-09-17 20:14:29,128 [    pgoapi] [ INFO] Execution of RPC
2016-09-17 20:14:29,128 [   rpc_api] [WARNING] Argument attack_actions with value {'action_start_ms': 1474136067212L, 'target_index': -1, 'damage_windows_end_timestamp_mss': 1474136067612L, 'damage_windows_start_timestamp_mss': 1474136067412L, 'duration_ms': 400, 'Type': 1, 'active_pokemon_id': 12138959532846654465L} unknown inside AttackGymMessage (Exception: 'RepeatedCompositeFieldContainer' object has no attribute 'append')
2016-09-17 20:14:29,128 [   rpc_api] [WARNING] Argument attack_actions with value {'action_start_ms': 1474136067677L, 'target_index': -1, 'damage_windows_end_timestamp_mss': 1474136068077L, 'damage_windows_start_timestamp_mss': 1474136067877L, 'duration_ms': 400, 'Type': 1, 'active_pokemon_id': 12138959532846654465L} unknown inside AttackGymMessage (Exception: 'RepeatedCompositeFieldContainer' object has no attribute 'append')
2016-09-17 20:14:29,128 [   rpc_api] [WARNING] Argument attack_actions with value {'action_start_ms': 1474136068142L, 'target_index': -1, 'damage_windows_end_timestamp_mss': 1474136068542L, 'damage_windows_start_timestamp_mss': 1474136068342L, 'duration_ms': 400, 'Type': 1, 'active_pokemon_id': 12138959532846654465L} unknown inside AttackGymMessage (Exception: 'RepeatedCompositeFieldContainer' object has no attribute 'append')
2016-09-17 20:14:29,128 [   rpc_api] [WARNING] Argument attack_actions with value {'action_start_ms': 1474136068607L, 'target_index': -1, 'damage_windows_end_timestamp_mss': 1474136069007L, 'damage_windows_start_timestamp_mss': 1474136068807L, 'duration_ms': 400, 'Type': 1, 'active_pokemon_id': 12138959532846654465L} unknown inside AttackGymMessage (Exception: 'RepeatedCompositeFieldContainer' object has no attribute 'append')
2016-09-17 20:14:29,381 [    pgoapi] [ INFO] Cleanup of request!

The actions I'm trying to send out never appear in the battle log, therefore, this should be the source of my problem right?
How do I print the request that I'm sending out, like shown in the battle log from the pastebin? I only found how to print the server responses.
Given the warning, I'm probably passing the actions in a wrong way, even though I'm using @Nostrademous code snippet.

while (battleTime - startTime) < 1500.0:
        battleAction = createQuickAttack(battleTime, qm_dur, currAttacker, qm_ed)

        battleTime += (qm_dur + 65)
        actions.append( protobuf_to_dict(battleAction ))

#     lastAction = protobuf_to_dict(last_action) # This doesn't work because I only have last_action in #form of a dict

    lastAction = last_action

    while get_time(True) < battleTime: time.sleep(0.1)
    log.info('Current Time: %d' % (get_time(True)))

    attack_resp = api.attack_gym(gym_id=gym_id, battle_id=battle_id, attack_actions=actions, last_retrieved_actions=lastAction, player_latitude=position[0], player_longitude=position[1])



def createQuickAttack(stime, move_duration, currAttacker, energy):
    return BattleAction(Type=1, action_start_ms=stime, duration_ms=move_duration, target_index=-1, active_pokemon_id=currAttacker, damage_windows_start_timestamp_mss=(stime+move_duration-200), damage_windows_end_timestamp_mss=(stime+move_duration))

Also, I can't seem to get the last_action into the message either.
In the function of the code snippet, the last_action seems to be of type BattleAction. However, I only got it in the form of a dict from the server response, which is why I can't run protobuf_to_dict() on it
Passing it as a dict to api.attack_gym() gives me following warning:

    2016-09-17 21:03:03,210 [   rpc_api] [WARNING] Argument last_retrieved_actions with value {'player_joined': {'trainer_public_profile':
{'name': u'mecaawsd', 'avatar': {}, 'level': 22}, 'active_pokemon': {'pokemon_data': {'num_upgrades': 6, 'move_1': 215, 'move_2': 47, '
additional_cp_multiplier': 0.04325294494628906, 'pokeball': 3, 'pokemon_id': 3, 'creation_time_ms': 1472506234728L, 'height_m': 2.06883
8596343994, 'stamina_max': 106, 'weight_kg': 109.53936004638672, 'individual_defense': 6, 'cp_multiplier': 0.5974000096321106, 'stamina
': 106, 'battles_attacked': 249, 'individual_stamina': 7, 'cp': 1507, 'id': 12138959532846654465L}, 'current_health': 106}}, 'Type': 6,
 'action_start_ms': 1474138981277L, 'target_index': -1} unknown inside AttackGymMessage (Exception: Assignment not allowed to composite
 field "player_joined" in protocol message object.)

Should I try making a BattleAction out of the last_action dictionary and then pass it to protobuf_to_dict in order to pass it to api.attack_gym()?

Please somebody help :(

@firebolt55439
Copy link

@ConstiZ Read this thread: Grover-c13/PokeGOAPI-Java#252

It should contain all necessary information to fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

12 participants