Glitch City Laboratories Archives

Glitch City Laboratories closed on 1 September 2020 (announcement). This is an archived copy of a thread from Glitch City Laboratories Forums.

You can join Glitch City Research Institute to ask questions or discuss current developments.

You may also download the archive of this forum in .tar.gz, .sql.gz, or .sqlite.gz formats.

Programming/Scripting/Development/Web Design

Pokemon Battle Simulator (WIP) - Page 1

Pokemon Battle Simulator (WIP)

Posted by: OldNewComputers
Date: 2019-11-07 19:17:24
As a programming project, I'm writing a Pokemon battle simulator in Python. It still lacks status conditions and most move effects (I am using the pret disassembly and some data extraction tools I wrote to get most of this info, plus some diagrams on Bulbapedia), but I wanted your guys opinion on the code. Keep in mind that, to run it, you need a Pokemon Red/Blue ROM with the name "pokeblue.gb" in the same directory as this file, and I wanted the opinion of you guys on the code. I'm not sure if I'm targeting bug compatibility with anything (including Stadium or RB/Y), but I am targeting Generation 1 functionality. Beware that it is pretty long, and liable only to get longer. Most of the code is in the "mainBattle" function, but that calls several utility functions, and several other functions serve to datamine or create monsters for use in the game. Summary printing works too.
#pokemon battle simulator - gen 1
import random
import math
file = open('pokeblue.gb','rb')
file_contents = file.read()
file.close()

typeMatchups = {0: {5: 0.5, 8: 0.0}, 1: {0: 2.0, 3: 0.5, 2: 0.5, 24: 0.5, 7: 0.5, 5: 2.0, 25: 2.0, 8: 0.0}, 2: {23: 0.5, 1: 2.0, 7: 2.0, 22: 2.0, 5: 0.5}, 3: {22: 2.0, 3: 0.5, 4: 0.5, 7: 2.0, 5: 0.5, 8: 0.5}, 4: {2: 0.0, 20: 2.0, 23: 2.0, 22: 0.5, 7: 0.5, 5: 2.0, 3: 2.0}, 5: {20: 2.0, 1: 0.5, 4: 0.5, 2: 2.0, 7: 2.0, 25: 2.0}, 6: {}, 7: {20: 0.5, 22: 2.0, 1: 0.5, 2: 0.5, 24: 2.0, 8: 0.5, 3: 2.0}, 8: {8: 2.0, 0: 0.0, 24: 0.0}, 9: {}, 10: {}, 11: {}, 12: {}, 13: {}, 14: {}, 15: {}, 16: {}, 17: {}, 18: {}, 19: {}, 20: {22: 2.0, 25: 2.0, 20: 0.5, 21: 0.5, 7: 2.0, 5: 0.5, 26: 0.5}, 21: {20: 2.0, 5: 2.0, 21: 0.5, 22: 0.5, 4: 2.0, 26: 0.5}, 22: {21: 2.0, 22: 0.5, 20: 0.5, 4: 2.0, 7: 0.5, 3: 0.5, 5: 2.0, 2: 0.5, 26: 0.5}, 23: {21: 2.0, 23: 0.5, 22: 0.5, 4: 0.0, 2: 2.0, 26: 0.5}, 24: {24: 0.5, 1: 2.0, 3: 2.0}, 25: {25: 0.5, 21: 0.5, 22: 2.0, 4: 2.0, 2: 2.0, 26: 2.0}, 26: {26: 2.0}}
typeNames = {0: 'NORMAL', 1: "FIGHTING", 2: "FLYING", 3: "POISON", 4: 'GROUND',
            5: "ROCK", 6: "BIRD", 7: "BUG", 8: "GHOST", 0x14: "FIRE",
            0x15: 'WATER', 0x16: "GRASS", 0x17: 'ELECTRIC', 0x18: "PSYCHIC",
            0x19: 'ICE', 0x1A: 'DRAGON'}

volatileStatus0 = [0]*8 #confusion, recharge, wrap, leechseed, flinch
volatileStatus1 = [0]*8

statChanges0 = [0, 0, 0, 0, 0, 0] #attack, defense, speed, special, accuracy, evasion
statChanges1 = [0, 0, 0, 0, 0, 0]


statChangeList = {-6: 0.25, -5: 0.28, -4: 1/3, -3: 0.4, -2: 0.5, -1: 2/3, 0: 1,
                  1: 1.5, 2: 2, 3: 2.5, 4: 3, 5: 3.5, 6: 4}

indexToDex = []
dexToIndex = [112, 115, 32, 35, 21, 100, 34, 80, 2, 103, 108, 102, 88, 94, 29, 31, 104, 111, 131, 59, 151, 130, 90, 72, 92, 123, 120, 9, 127, 114, 0, 0, 58, 95, 22, 16, 79, 64, 75, 113, 67, 122, 106, 107, 24, 47, 54, 96, 76, 0, 126, 0, 125, 82, 109, 0, 56, 86, 50, 128, 0, 0, 0, 83, 48, 149, 0, 0, 0, 84, 60, 124, 146, 144, 145, 132, 52, 98, 0, 0, 0, 37, 38, 25, 26, 0, 0, 147, 148, 140, 141, 116, 117, 0, 0, 27, 28, 138, 139, 39, 40, 133, 136, 135, 134, 66, 41, 23, 46, 61, 62, 13, 14, 15, 0, 85, 57, 51, 49, 87, 0, 0, 10, 11, 12, 68, 0, 55, 97, 42, 150, 143, 129, 0, 0, 89, 0, 99, 91, 0, 101, 36, 110, 53, 105, 0, 93, 63, 65, 17, 18, 121, 1, 3, 73, 0, 118, 119, 0, 0, 0, 0, 77, 78, 19, 20, 33, 30, 74, 137, 142, 0, 81, 0, 0, 4, 7, 5, 8, 6, 0, 0, 0, 0, 43, 44, 45, 69, 70, 71]###

a = [21, 54, 1, 100, 0, 24, 24, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 54, 1, 205, 0, 205, 0, 205, 0, 205, 0]
b = [126, 34, 1, 100, 0, 1, 1, 0, 1, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 34, 1, 9, 1, 165, 0, 115, 0, 135, 0]
c = [21, 147, 1, 100, 0, 24, 24, 0, 94, 1, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 35, 0, 0, 0, 147, 1, 42, 1, 42, 1, 42, 1, 42, 1]
                                                                                                    #10  35
player_old = [c,b,a]
enemy_old = ['ROCKET',[a,b]]

currentPkmn = 0
currentEMon = 0

pidgeot_gary = [151, 228, 0, 61, 0, 0, 2, 0, 17, 119, 143, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 20, 5, 20, 0, 228, 0, 159, 0, 153, 0, 172, 0, 147, 0]
alakazam_gary = [149, 188, 0, 59, 0, 24, 24, 0, 60, 94, 115, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 10, 20, 20, 0, 188, 0, 118, 0, 112, 0, 201, 0, 219, 0]
rhydon_gary = [1, 255, 0, 61, 0, 4, 5, 0, 43, 39, 31, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 20, 5, 0, 255, 0, 220, 0, 208, 0, 110, 0, 116, 0]
arcanine_gary = [20, 237, 0, 61, 0, 20, 20, 0, 46, 43, 52, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 25, 20, 0, 237, 0, 195, 0, 159, 0, 177, 0, 159, 0]
exeggutor_gary = [10, 251, 0, 63, 0, 22, 24, 0, 95, 140, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 0, 0, 251, 0, 183, 0, 170, 0, 132, 0, 221, 0]
blastoise_gary = [28, 238, 0, 65, 0, 21, 21, 0, 56, 59, 44, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 25, 40, 0, 238, 0, 173, 0, 195, 0, 166, 0, 175, 0]

test_charizard = [180, 233, 0, 64, 0, 20, 2, 0, 99, 163, 53, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 15, 15, 0, 233, 0, 172, 0, 164, 0, 192, 0, 173, 0]
test_raichu = [85, 210, 0, 64, 0, 23, 23, 0, 85, 86, 97, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 20, 30, 20, 0, 210, 0, 179, 0, 134, 0, 192, 0, 179, 0]
test_tauros = [60, 229, 0, 64, 0, 0, 0, 0, 36, 43, 99, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 20, 30, 0, 229, 0, 192, 0, 186, 0, 205, 0, 154, 0]
test_gyarados = [22, 255, 0, 64, 0, 21, 2, 0, 56, 82, 43, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 30, 5, 0, 255, 0, 224, 0, 165, 0, 168, 0, 192, 0]

player_test = [test_charizard, test_raichu, test_tauros, test_gyarados] #add mons
enemy_gary = ["BLUE",[pidgeot_gary, alakazam_gary, rhydon_gary, arcanine_gary, exeggutor_gary, blastoise_gary]] #add mons

moveNames = """POUND
KARATE CHOP
DOUBLESLAP
COMET PUNCH
MEGA PUNCH
PAY DAY
FIRE PUNCH
ICE PUNCH
THUNDERPUNCH
SCRATCH
VICEGRIP
GUILLOTINE
RAZOR WIND
SWORDS DANCE
CUT
GUST
WING ATTACK
WHIRLWIND
FLY
BIND
SLAM
VINE WHIP
STOMP
DOUBLE KICK
MEGA KICK
JUMP KICK
ROLLING KICK
SAND-ATTACK
HEADBUTT
HORN ATTACK
FURY ATTACK
HORN DRILL
TACKLE
BODY SLAM
WRAP
TAKE DOWN
THRASH
DOUBLE-EDGE
TAIL WHIP
POISON STING
TWINEEDLE
PIN MISSILE
LEER
BITE
GROWL
ROAR
SING
SUPERSONIC
SONICBOOM
DISABLE
ACID
EMBER
FLAMETHROWER
MIST
WATER GUN
HYDRO PUMP
SURF
ICE BEAM
BLIZZARD
PSYBEAM
BUBBLEBEAM
AURORA BEAM
HYPER BEAM
PECK
DRILL PECK
SUBMISSION
LOW KICK
COUNTER
SEISMIC TOSS
STRENGTH
ABSORB
MEGA DRAIN
LEECH SEED
GROWTH
RAZOR LEAF
SOLARBEAM
POISONPOWDER
STUN SPORE
SLEEP POWDER
PETAL DANCE
STRING SHOT
DRAGON RAGE
FIRE SPIN
THUNDERSHOCK
THUNDERBOLT
THUNDER WAVE
THUNDER
ROCK THROW
EARTHQUAKE
FISSURE
DIG
TOXIC
CONFUSION
PSYCHIC
HYPNOSIS
MEDITATE
AGILITY
QUICK ATTACK
RAGE
TELEPORT
NIGHT SHADE
MIMIC
SCREECH
DOUBLE TEAM
RECOVER
HARDEN
MINIMIZE
SMOKESCREEN
CONFUSE RAY
WITHDRAW
DEFENSE CURL
BARRIER
LIGHT SCREEN
HAZE
REFLECT
FOCUS ENERGY
BIDE
METRONOME
MIRROR MOVE
SELFDESTRUCT
EGG BOMB
LICK
SMOG
SLUDGE
BONE CLUB
FIRE BLAST
WATERFALL
CLAMP
SWIFT
SKULL BASH
SPIKE CANNON
CONSTRICT
AMNESIA
KINESIS
SOFTBOILED
HI JUMP KICK
GLARE
DREAM EATER
POISON GAS
BARRAGE
LEECH LIFE
LOVELY KISS
SKY ATTACK
TRANSFORM
BUBBLE
DIZZY PUNCH
SPORE
FLASH
PSYWAVE
SPLASH
ACID ARMOR
CRABHAMMER
EXPLOSION
FURY SWIPES
BONEMERANG
REST
ROCK SLIDE
HYPER FANG
SHARPEN
CONVERSION
TRI ATTACK
SUPER FANG
SLASH
SUBSTITUTE
STRUGGLE"""

pokeNames = ['RHYDON', 'KANGASKHAN', 'NIDORAN', 'CLEFAIRY', 'SPEAROW', 'VOLTORB', 'NIDOKING', 'SLOWBRO', 'IVYSAUR', 'EXEGGUTOR', 'LICKITUNG', 'EXEGGCUTE', 'GRIMER', 'GENGAR', 'NIDORAN', 'NIDOQUEEN', 'CUBONE', 'RHYHORN', 'LAPRAS', 'ARCANINE', 'MEW', 'GYARADOS', 'SHELLDER', 'TENTACOOL', 'GASTLY', 'SCYTHER', 'STARYU', 'BLASTOISE', 'PINSIR', 'TANGELA', 'MISSINGNO.', 'MISSINGNO.', 'GROWLITHE', 'ONIX', 'FEAROW', 'PIDGEY', 'SLOWPOKE', 'KADABRA', 'GRAVELER', 'CHANSEY', 'MACHOKE', 'MR.MIME', 'HITMONLEE', 'HITMONCHAN', 'ARBOK', 'PARASECT', 'PSYDUCK', 'DROWZEE', 'GOLEM', 'MISSINGNO.', 'MAGMAR', 'MISSINGNO.', 'ELECTABUZZ', 'MAGNETON', 'KOFFING', 'MISSINGNO.', 'MANKEY', 'SEEL', 'DIGLETT', 'TAUROS', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', "FARFETCH'D", 'VENONAT', 'DRAGONITE', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'DODUO', 'POLIWAG', 'JYNX', 'MOLTRES', 'ARTICUNO', 'ZAPDOS', 'DITTO', 'MEOWTH', 'KRABBY', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'VULPIX', 'NINETALES', 'PIKACHU', 'RAICHU', 'MISSINGNO.', 'MISSINGNO.', 'DRATINI', 'DRAGONAIR', 'KABUTO', 'KABUTOPS', 'HORSEA', 'SEADRA', 'MISSINGNO.', 'MISSINGNO.', 'SANDSHREW', 'SANDSLASH', 'OMANYTE', 'OMASTAR', 'JIGGLYPUFF', 'WIGGLYTUFF', 'EEVEE', 'FLAREON', 'JOLTEON', 'VAPOREON', 'MACHOP', 'ZUBAT', 'EKANS', 'PARAS', 'POLIWHIRL', 'POLIWRATH', 'WEEDLE', 'KAKUNA', 'BEEDRILL', 'MISSINGNO.', 'DODRIO', 'PRIMEAPE', 'DUGTRIO', 'VENOMOTH', 'DEWGONG', 'MISSINGNO.', 'MISSINGNO.', 'CATERPIE', 'METAPOD', 'BUTTERFREE', 'MACHAMP', 'MISSINGNO.', 'GOLDUCK', 'HYPNO', 'GOLBAT', 'MEWTWO', 'SNORLAX', 'MAGIKARP', 'MISSINGNO.', 'MISSINGNO.', 'MUK', 'MISSINGNO.', 'KINGLER', 'CLOYSTER', 'MISSINGNO.', 'ELECTRODE', 'CLEFABLE', 'WEEZING', 'PERSIAN', 'MAROWAK', 'MISSINGNO.', 'HAUNTER', 'ABRA', 'ALAKAZAM', 'PIDGEOTTO', 'PIDGEOT', 'STARMIE', 'BULBASAUR', 'VENUSAUR', 'TENTACRUEL', 'MISSINGNO.', 'GOLDEEN', 'SEAKING', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'PONYTA', 'RAPIDASH', 'RATTATA', 'RATICATE', 'NIDORINO', 'NIDORINA', 'GEODUDE', 'PORYGON', 'AERODACTYL', 'MISSINGNO.', 'MAGNEMITE', 'MISSINGNO.', 'MISSINGNO.', 'CHARMANDER', 'SQUIRTLE', 'CHARMELEON', 'WARTORTLE', 'CHARIZARD', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'MISSINGNO.', 'ODDISH', 'GLOOM', 'VILEPLUME', 'BELLSPROUT', 'WEEPINBELL', 'VICTREEBEL']

def createReverseList(lst):
    dict = {}
    for i in range(len(pokeNames)):
        dict[pokeNames[i]] = i+1
    return dict

revPN = createReverseList(pokeNames)

def findTable():
    address = 0

    for i in file_contents:
        if file_contents[address] == 112 and file_contents[address+1] == 115 and file_contents[address+2] == 32 and file_contents[address+3] == 35: return address
        address += 1

def grabTable(address):
    table = []

    for i in range(0, 190):
        table.append(file_contents[address + i])
   
    return table

def convertNameToIndex(moveName):
    string = moveNames.split("\n")
    index = 0

    for i in string:
        if i == moveName: return index + 1
        index += 1

def physOrSpcl(type):
    if type > 16:
        return 1

    else:
        return 0

def getTE(type0, type1):
    try: return typeMatchups[type0][type1]
    except: return 1

def readBaseStats(pokedex):
    if pokedex in range(1, 151):
        offset = 0x383de + 28*(pokedex-1)
    else:
        offset = 0x425b

    species = []

    for i in range(0, 28):
        species.append(file_contents[offset + i])
       
    return species

def readMoveData(moveID, po=0):
    offset = 0x38000 + 6*(moveID-1)

    if po:
        print("Index Number: {}".format(file_contents[offset]))
        print("Effect ID: {}".format(file_contents[offset+1]))
        print('Attack Power: {}'.format(file_contents[offset+2]))
        print("Move Type: {}".format(file_contents[offset+3]))
        print("Accuracy: {}".format(file_contents[offset+4]))
        print("PP: {}".format(file_contents[offset+5]))

    return (file_contents[offset], file_contents[offset+1], file_contents[offset+2],
            file_contents[offset+3], file_contents[offset+4], file_contents[offset+5])


def indvCalc(B, E, I, L):
    return int(int((2 * B + (I*2) + E) * L / 100 + 5))

def statCalc(evs, ivs, dex, level): #evs = [0,0,0,0,0], ivs = [0,0,0,0]
    stats = [0] * 5
    #generate hp iv
    hp_dv = 0
    add = 8
    for i in ivs:
        if not (i % 2 == 0): hp_dv += add
        add /= 2
    #calculate E value in format E_HP, E_A
    E = [0] * 5
    for i in range(0, 5):
        E[i] = int(min(255, int(math.sqrt(max(0, evs[i] - 1)) + 1)) / 4)

    #calculate all stats
    for i in range(1, 5):
        stats[i] = indvCalc(readBaseStats(dex)[i+1], E[i], ivs[i-1], level)
    #calculate hp
    stats[0] = int((2 * readBaseStats(dex)[1] + (hp_dv*2) + E[0]) * level / 100 + level + 10)
   
    return stats

def monsterCreator(species):
    pokemon = [species] + ([0] * 43)
    #get base stats
    basestats = readBaseStats(dexToIndex[species - 1])
    #enter level, evs, ivs
    pokemon[3] = int(input("Enter level: "))
    evs = [int(input("Enter HP EV: ")), int(input("Enter Attack EV: ")), int(input("Enter Defense EV: ")), int(input("Enter Speed EV: ")), int(input("Enter Special EV: "))]
    ivs = [int(input("Enter Attack IV: ")), int(input("Enter Defense IV: ")), int(input("Enter Speed IV: ")), int(input("Enter Special IV: ")), ]
    #enter moves
    num = int(input('Enter number of moves: '))
   
    for i in range(num):
        move = input("Enter move name: ").upper()
        move = convertNameToIndex(move)
        pokemon[8+i] = move
        pokemon[0x1d+i] = readMoveData(move)[5]

    #calculate stats
    stats = statCalc(evs, ivs, dexToIndex[species - 1], pokemon[3])

    for i in range(0, 5):
        upper = stats[i] >> 8
        lower = stats[i] & 255

        pokemon[0x22+(2*i)] = lower
        pokemon[0x23+(2*i)] = upper
    pokemon[1] = pokemon[0x22]
    pokemon[2] = pokemon[0x23]
    #assign type 1, type 2
    pokemon[5] = basestats[6]
    pokemon[6] = basestats[7]
   
    #return value
    return pokemon

def getMoves(pokemon):
    moveList = ["-"] + moveNames.split("\n")
    list = []
    empty = []
    #print("1. {} | {} | {}/{} PP".format(moveList[pokemon[8]], typeNames[readMoveData(pokemon[8])[3]], pokemon[0x1d], readMoveData(pokemon[8])[5]))
    if pokemon[0x1d] == 0: empty.append("1")
    else: list.append("1")

    if pokemon[9] != 0:
        #print("2. {} | {} | {}/{} PP".format(moveList[pokemon[9]], typeNames[readMoveData(pokemon[9])[3]], pokemon[0x1e], readMoveData(pokemon[9])[5]))
        if pokemon[0x1e] == 0: empty.append("2")
        else: list.append("2")

    if pokemon[10] != 0:
        #print("3. {} | {} | {}/{} PP".format(moveList[pokemon[10]], typeNames[readMoveData(pokemon[10])[3]], pokemon[0x1f], readMoveData(pokemon[10])[5]))
        if pokemon[0x1f] == 0: empty.append("3")
        else: list.append("3")

    if pokemon[11] != 0:
        #("4. {} | {} | {}/{} PP".format(moveList[pokemon[11]], typeNames[readMoveData(pokemon[11])[3]], pokemon[0x20], readMoveData(pokemon[11])[5]))
        if pokemon[0x20] == 0: empty.append("4")
        else: list.append("4")
       
    return list, empty


def printMoves(pokemon):
    moveList = ["-"] + moveNames.split("\n")
    list = []
    empty = []
    print("1. {} | {} | {}/{} PP".format(moveList[pokemon[8]], typeNames[readMoveData(pokemon[8])[3]], pokemon[0x1d], readMoveData(pokemon[8])[5]))
    if pokemon[0x1d] == 0: empty.append("1")
    else: list.append("1")

    if pokemon[9] != 0:
        print("2. {} | {} | {}/{} PP".format(moveList[pokemon[9]], typeNames[readMoveData(pokemon[9])[3]], pokemon[0x1e], readMoveData(pokemon[9])[5]))
        if pokemon[0x1e] == 0: empty.append("2")
        else: list.append("2")

    if pokemon[10] != 0:
        print("3. {} | {} | {}/{} PP".format(moveList[pokemon[10]], typeNames[readMoveData(pokemon[10])[3]], pokemon[0x1f], readMoveData(pokemon[10])[5]))
        if pokemon[0x1f] == 0: empty.append("3")
        else: list.append("3")

    if pokemon[11] != 0:
        print("4. {} | {} | {}/{} PP".format(moveList[pokemon[11]], typeNames[readMoveData(pokemon[11])[3]], pokemon[0x20], readMoveData(pokemon[11])[5]))
        if pokemon[0x20] == 0: empty.append("4")
        else: list.append("4")
       
    return list, empty

def retStatus(pokemon):
    statString = ''
    status = pokemon[4]
    hp = pokemon[1] + (pokemon[2] * 256)

    if status == 0 and hp == 0:
        statString = 'FNT'
    if status == 0 and hp > 0:
        statString = 'OK'
    if status == 4:
        statString = 'SLP'
    if status == 8:
        statString = 'PSN'
    if status == 16:
        statString = 'BRN'
    if status == 32:
        statString = 'FRZ'
    if status == 64:
        statString = 'PRZ'

    return statString


def printSummary(pokemon):
    #what we need to print - stats, types, species, moves
    print("Species: {} | Level {}".format(pokeNames[pokemon[0] - 1], pokemon[3]))
    print("HP: {}/{}".format(pokemon[0x1] + (pokemon[0x2] * 256), pokemon[0x22] + (pokemon[0x23] * 256)))
    print("Attack: {}".format(pokemon[0x24] + (pokemon[0x25] * 256)))
    print("Defense: {}".format(pokemon[0x26] + (pokemon[0x27] * 256)))
    print("Speed: {}".format(pokemon[0x28] + (pokemon[0x29] * 256)))
    print("Special: {}".format(pokemon[0x2A] + (pokemon[0x2B] * 256)))

    print('Status: {}'.format(retStatus(pokemon)))

   
    if pokemon[5] != pokemon[6]: print("Type: {}/{}".format(typeNames[pokemon[5]], typeNames[pokemon[6]])) #add check for single/dual type
    else: print ('Type: {}'.format(typeNames[pokemon[5]]))
    #add attacks
    printMoves(pokemon)
   
    return

def accuracyTest(moveID):
    result = random.randint(0, 256)
    accuracy = readMoveData(moveID)[4] * statChangeList[statChanges0[4]] * statChangeList[0-(statChanges1[5])]

    if accuracy >= result: return 0
    return 1


def printParty(party,p=1):
    counter = 1
    valids = []
    fainted = []
    for i in party:
        stat = retStatus(i)
        if p: print("{}. {} | {} | Level {} | {}/{}HP".format(counter, pokeNames[i[0]-1], stat, i[3], i[0x1] + (i[0x2] * 256), i[0x22] + (i[0x23] * 256))) #add level

        if stat == "FNT":
            fainted.append(str(counter))
        else:
            valids.append(str(counter))

        counter += 1
       
    return valids,fainted


def damageCalculator(attacker, target, moveID):
    global random
    stab = 1
    #modifier = random * stab * type
    #damage = ( ( ( ( (  (2*level) / 5  ) + 2  ) * power * a/d  )/50 ) + 2 ) * modifier

    ###assign vars - attacker level, power, acting attack/defense
    level = attacker[3] #implement critical hits, doubles level, based on speed
    power = readMoveData(moveID)[2]
    result = physOrSpcl(readMoveData(moveID)[3])

    if result:
        a = (attacker[0x2a] + (attacker[0x2b] * 256)) * statChangeList[statChanges0[3]]
        d = (target[0x2a] + (target[0x2b] * 256)) * statChangeList[statChanges1[3]]
    if not result:
        if attacker[4] == 16: a = (attacker[0x24] + (attacker[0x25] * 256)) * statChangeList[statChanges0[0]] * 0.25 #* (0.25*int(attacker[4]==16))
        else: a = (attacker[0x24] + (attacker[0x25] * 256)) * statChangeList[statChanges0[0]]
        d = (target[0x26] + (target[0x27] * 256)) * statChangeList[statChanges1[1]]

    ###calculate modifier
    #calculate random
    rnd = random.randint(217,255) / 255
    #calculate stab
    if readMoveData(moveID)[3] in (attacker[5], attacker[6]):
        stab = 1.5
    #calculate type effectiveness
    if target[5] == target[6]:
        #type = typeMatchups[readMoveData(moveID)[3]][target[5]]
        type = getTE(readMoveData(moveID)[3], target[5])
    if target[5] != target[6]:
        type = getTE(readMoveData(moveID)[3], target[5]) * getTE(readMoveData(moveID)[3], target[6])
        #type = typeMatchups[readMoveData(moveID)[3]][target[5]] * typeMatchups[readMoveData(moveID)[3]][target[6]]

    if type == 0:
        print("It had no effect!")
    if type < 1 and type != 0:
        print("It's not very effective...")
    if type > 1:
        print("It's super effective!")

    modifier = rnd * stab * type
    damage = ( ( ( ( (  (2*level) / 5  ) + 2  ) * power * a/d  )/50 ) + 2 ) * modifier
    #print("Level:{},Power:{},A:{},D:{}".format(level,power,a,d))


    return int(damage)

def resolveMove(attacker, target, move, entry, who=0): #48=recoil, 29=2to5hits
    #add checks for 0 basepower, non-damaging effects
    ret_value = [0, 0] #value 0 = target fainted, value 1 = attacker fainted
    acc = accuracyTest(move)
    if not acc and readMoveData(move)[2] != 0 and readMoveData(move)[1] != 29 and readMoveData(move)[1] != 38:
        damage = damageCalculator(attacker, target, move)
        enemyHP = target[1] + (target[2] * 256)
        #print("{} damage!".format(damage)) #inflict damage, add faint check
        enemyHP -= damage
        if enemyHP < 0: enemyHP = 0
        upper = enemyHP >> 8
        lower = enemyHP & 255
        target[1] = lower
        target[2] = upper
    elif not acc and readMoveData(move)[1] == 38:
        print("It's a one-hit-KO!")
        target[1] = 0; target[2] = 0
    else:
        print("The attack missed...")

    if readMoveData(move)[1] == 48:
        playerHP = attacker[1] + (attacker[2] * 256)
        recoil = int(damage/4)
        print("{} recoil!".format(recoil))
        playerHP -= recoil
        if playerHP < 0: playerHP = 0
        upper = playerHP >> 8
        lower = playerHP & 255
        attacker[1] = lower
        attacker[2] = upper

    if readMoveData(move)[1] == 71: #special down
        down = random.randint(1,10)
        if down == 10:
            if not who:
                statChanges1[3] -= 1
                print("Enemy {}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

            if who: #enemy using attack
                statChanges0[3] -= 1
                print("{}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

            #print("{}'s SPECIAL stat went down!".format(pokeNames[target[0]-1]))

    if entry != -1:
        attacker[0x1c+entry] -= 1

    if target[1] == 0 and target[2] == 0:
        ret_value[(not who)] = 1
    if attacker[1] == 0 and attacker[2] == 0:
        ret_value[(who)] = 1
    return ret_value



def mainBattle(playerParty, enemy): #list of pokes
    global currentPkmn
    global currentEMon
    global volatileStatus0
    global volatileStatus1
    global statChanges0
    global statChanges1
    moveNameList = [""] + moveNames.split("\n")
    attacking = 0 #0=not,1=attacking,2=struggle
    enemyAttacking = 0
    attack = [0,0]
    enemyAttack = [0,0]
    print("{} WANTS TO FIGHT!".format(enemy[0]))
    enemyParty = enemy[1]

    print("GO, {}!".format(pokeNames[playerParty[0][0]-1]))
    print("{} SENT OUT {}!".format(enemy[0],  pokeNames[enemy[1][0][0]-1]    ))

    while True: #mainbattle loop, add flinch, do speedtest
        enemy_maxhp = enemyParty[currentEMon][0x22] + (enemyParty[currentEMon][0x23] * 256)
        enemy_chp = enemyParty[currentEMon][0x1] + (enemyParty[currentEMon][0x2] * 256)
        enemy_php = (enemy_chp / enemy_maxhp) * 100
        enemy_php = round(enemy_php, 0)
        player_maxhp = playerParty[currentPkmn][0x22] + (playerParty[currentPkmn][0x23] * 256)
        player_chp = playerParty[currentPkmn][0x1] + (playerParty[currentPkmn][0x2] * 256)
        stat = retStatus(playerParty[currentPkmn])
        enemyStat = retStatus(enemyParty[currentEMon])
        print("{} | {} | Level {} | {}/{}HP".format(pokeNames[playerParty[currentPkmn][0]-1], stat, playerParty[currentPkmn][3], player_chp, player_maxhp)) #add hp, level
        print('Enemy: {} | {} | Level {} | {}% HP'.format(pokeNames[enemy[1][currentEMon][0]-1], enemyStat, enemyParty[currentEMon][3], enemy_php)) #add hp, level
        print("")

        if volatileStatus0[1] == 0: #add wrap check

            while True:
                attacking = 0
                attack = [0,0]
                choice = input('''Please choice an option
1.) Fight (Choose an attack)
2.) Switch Pokemon
? ''')
                if choice == "1":
                    options = getMoves(playerParty[currentPkmn])

                    if options[0] == []:
                        print("{} ran out of moves! {} used STRUGGLE!".format(pokeNames[playerParty[currentPkmn][0]-1], pokeNames[playerParty[currentPkmn][0]-1]))
                        #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1)
                        attacking = 2
                        break
                   
                    else: #add freeze, sleep, confusion checks
                        options = printMoves(playerParty[currentPkmn])
                        print("C. Cancel")
                        pkmn = input("? ")

                        while pkmn not in options[0]:
                            #print(options[0])
                            if pkmn in options[1]: print("The move is out of PP!")
                            elif pkmn == "C": break
                            else: print("Out of range. Re-enter move number to use.")
                            pkmn = input('? ').upper()

                        if pkmn in options[0]:
                            #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn))
                            attacking = 1
                            attack = (playerParty[currentPkmn][int(pkmn)+7],int(pkmn))
                            break

                        continue
                       

                if choice == "2": #add summary
                    options = printParty(playerParty)
                    print("C. Cancel")
                    pkmn = input("? ").upper()

                    while pkmn not in options[0]:
                        if pkmn in options[1]: print("There's no energy left to battle!")
                        elif pkmn == "C": break
                        else: print("Out of range. Re-enter Pokemon number to switch.")
                        pkmn = input('? ').upper()

                    if pkmn in options[0]:
                        volatileStatus0 = [0]*8
                        statChanges0 = [0]*6
                        print("Enough, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                        currentPkmn = int(pkmn)-1
                        print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                        break

                    continue

        else:
            print("{} needs to recharge!".format(pokeNames[playerParty[currentPkmn][0]-1]))







        #do enemy input
        if volatileStatus1[1] == 0: #add wrap check

            while True:
                options = printParty(enemyParty,0)
                enemyAttacking = 0
                enemyAttack = [0,0]
                if len(options[0]) > 1: choice = random.choice(["1", "2"])
                else: choice = "1"
                if choice == "1":
                    options = getMoves(enemyParty[currentEMon])

                    if options[0] == []:
                        print("{} ran out of moves! {} used STRUGGLE!".format(pokeNames[enemyParty[currentEMon][0]-1], pokeNames[enemyParty[currentEMon][0]-1]))
                        #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1)
                        enemyAttacking = 2
                        break
                   
                    else: #add freeze, sleep, confusion checks
                        #options = printMoves(playerParty[currentPkmn])
                        #print("C. Cancel")
                        ekmn = random.choice(options[0])

                        while ekmn not in options[0]:
                            #print(options[0])
                            if ekmn in options[1]: pass
                            elif ekmn == "C": break
                            else: pass#rint("Out of range. Re-enter move number to use.")
                            ekmn = random.choice(options[0])

                        if ekmn in options[0]:
                            #resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn))
                            enemyAttacking = 1
                            attack = (enemyParty[currentEMon][int(ekmn)+7],int(ekmn))
                            break

                        continue
                       

                if choice == "2": #add summary
                    #print("C. Cancel")
                    ekmn = random.choice(options[0])

                    while ekmn not in options[0]:
                        if ekmn in options[1]: pass#rint("There's no energy left to battle!")
                        elif ekmn == "C": break
                        else: pass#rint("Out of range. Re-enter Pokemon number to switch.")
                        ekmn = input('? ').upper()

                    if ekmn in options[0]:
                        volatileStatus1 = [0]*8
                        statChanges1 = [0]*6
                        print("{} withdrew {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                        currentEMon = int(ekmn)-1
                        print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                        break

                    continue

        else:
            print("{} needs to recharge!".format(pokeNames[enemyParty[currentEMon][0]-1]))




        #add burn checks
        if playerParty[currentPkmn][4] == 16: #player burned
            print("{}'s hurt by the burn!")
            playerMaxHP = playerParty[currentPkmn][22] + (playerParty[currentPkmn][23] * 256)
            playerHP = playerParty[currentPkmn][1] + (playerParty[currentPkmn][2] * 256)
            playerHP -= int(playerMaxHP * (1/16))
            upper = playerHP >> 8
            lower = playerHP & 255
            playerParty[currentPkmn][1] = lower
            playerParty[currentPkmn][2] = upper
        ###faint check for burn check
           
        #input resolving time - add confusion, wrap tests
        result = [0,0]
        playerSpeed = (playerParty[currentPkmn][0x28] + (playerParty[currentPkmn][0x29] * 256)) * statChangeList[statChanges0[2]]
        enemySpeed = (enemyParty[currentEMon][0x28] + (enemyParty[currentEMon][0x29] * 256)) * statChangeList[statChanges1[2]]

        if playerSpeed == enemySpeed:
            rand = random.randint(1,2)
            if rand == 1: playerSpeed += 1
            if rand == 2: enemySpeed += 1

        if playerSpeed > enemySpeed: #player outspeeds enemy, include faint check
            if attacking == 1:
                print("{} used {}!".format(pokeNames[playerParty[currentPkmn][0]-1], moveNameList[playerParty[currentPkmn][int(pkmn)+7]]))
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn), 1)     
            if attacking == 2:
                print("{} is out of PP! {} used STRUGGLE!")
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1, 1)
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
            result = [0,0]
            #execute enemy attack
            if enemyAttacking == 1:
                print("Enemy {} used {}!".format(pokeNames[enemy[1][currentEMon][0]-1], moveNameList[enemyParty[currentEMon][int(ekmn)+7]]))
                result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], enemyParty[currentEMon][int(ekmn)+7], int(ekmn))
               
                #print(result)
            if enemyAttacking == 2: result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], 165, -1)
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))


            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            #faint check for enemy, player here

        if enemySpeed > playerSpeed: #enemy outspeeds player, include faint check
           
            if enemyAttacking == 1:
                print("Enemy {} used {}!".format(pokeNames[enemy[1][currentEMon][0]-1], moveNameList[enemyParty[currentEMon][int(ekmn)+7]]))
                result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], enemyParty[currentEMon][int(ekmn)+7], int(ekmn))
               
            if enemyAttacking == 2: result = resolveMove(enemyParty[currentEMon], playerParty[currentPkmn], 165, -1)
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))
            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
            result = [0,0]
            #execute player attack
            if attacking == 1:
                print("{} used {}!".format(pokeNames[playerParty[currentPkmn][0]-1], moveNameList[playerParty[currentPkmn][int(pkmn)+7]]))
                result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], playerParty[currentPkmn][int(pkmn)+7], int(pkmn), 1)
               
            if attacking == 2: result = resolveMove(playerParty[currentPkmn], enemyParty[currentEMon], 165, -1, 1)
           
            #faint check for enemy, player here
            if result[1] == 1: #player fainted
                print("{} fainted!".format(pokeNames[playerParty[currentPkmn][0]-1]))
                options = printParty(playerParty,1)
                #print(options)
                if options[0] == []:
                    print('You are out of usable Pokemon! Cradling your exchausted Pokemon, you scurry to a Pokemon Center. {} whited out!')
                    break
                else:
                    currentPkmn = 400000000000000000
                    attacking = 0
                    while str(currentPkmn) not in options[0]:
                        currentPkmn = int(input("? "))
                        if currentPkmn in options[1]: print("There's no energy left to battle!")
                    currentPkmn -= 1
                    volatileStatus0 = [0]*8
                    statChanges0 = [0]*6
                    print("Go, {}!".format(pokeNames[playerParty[currentPkmn][0]-1]))

            if result[0] == 1: #enemy fainted
                print("Enemy {} fainted!".format(pokeNames[enemy[1][currentEMon][0]-1]))
                options = printParty(enemyParty,0)
                #print(options)
                if options[0] == []:
                    print('{} is all out of usable Pokemon! {} has been defeated! You win!'.format(enemy[0], enemy[0]))
                    break
                else:
                    enemyAttacking = 0
                    currentEMon = int(random.choice(options[0]))-1
                    volatileStatus1 = [0]*8
                    statChanges1 = [0]*6
                    print("{} sent out {}!".format(enemy[0], pokeNames[enemy[1][currentEMon][0]-1]))
                   
        volatileStatus0[4] = 0 #reset flinch chance
        volatileStatus1[4] = 0

        #add leech seed, burn, poison check
    return