Exemple #1
0
def PickupReturn(seed, partySize):
    pickups = {}
    steps = 0
    # We return the number of steps to advance before trying again, to make
    # this more efficient (for example, if we didn't get any pickups, we know
    # we can skip the entire party size window instead of trying each extra
    # frame individually).
    retSteps = -1
    for i in range(partySize):
        seed = rng.advanceRng(seed, 1)
        steps += 1
        if rng.top(seed) % 10 != 0:
            continue
        # If we encounter a pickup in a slot other than the first, we should
        # try again with that pickup in the first slot, to maximize number of
        # potential pickups
        retSteps = steps - 1 if retSteps == -1 else retSteps
        seed = rng.advanceRng(seed, 1)
        steps += 1
        pickup = rng.top(seed) % 100
        if pickup >= 40 and pickup < 50:
            pickups['Ultra Ball'] = pickups.get('Ultra Ball', 0) + 1
        elif pickup >= 50 and pickup < 60:
            pickups['Rare Candy'] = pickups.get('Rare Candy', 0) + 1
        elif pickup >= 80 and pickup < 90:
            pickups['Nugget'] = pickups.get('Nugget', 0) + 1
        elif pickup >= 90 and pickup < 95:
            pickups['Protein'] = pickups.get('Protein', 0) + 1
        elif pickup >= 95 and pickup < 99:
            pickups['PP Up'] = pickups.get('PP Up', 0) + 1
        else:
            pickups['useless'] = pickups.get('useless', 0) + 1
    return (pickups, retSteps if retSteps != -1 else steps)
Exemple #2
0
def getTrendyWord(seed, index):
    "The half-word which describes a word to put in the trendy phrase."
    max_num = MAX_NUMBERS[index]
    seed = rng.advanceRng(seed, 1)
    value = rng.top(seed) % max_num
    index = (index & 0x7F) << 9
    result = value & 0x1FF
    return (seed, index | result)
Exemple #3
0
def getComparator(seed, injectVblank):
    """The half-word which is used to compare candidates."""
    # I have no idea why this individual bit needs it's own RNG call.
    seed = rng.advanceRng(seed, 1)
    halfword = 0 if rng.top(seed) & 1 == 0 else 0x40
    
    if injectVblank == 4:
        seed = rng.advanceRng(seed, 1)
    # This is how is makes a variable number of RNG calls.
    seed = rng.advanceRng(seed, 1)
    if rng.top(seed) % 0x62 > 0x32:
        if injectVblank == 5:
            seed = rng.advanceRng(seed, 1)
        seed = rng.advanceRng(seed, 1)
        if rng.top(seed) % 0x62 > 0x50:
            if injectVblank == 6:
                seed = rng.advanceRng(seed, 1)
            seed = rng.advanceRng(seed, 1)
    rand = rng.top(seed) % 0x62
    top7 = rand + 0x1E
    # The game also does a & 0x7F here, but we're guaranteed not to lose
    # information even without it.
    top7 <<= 7
    halfword |= top7
    
    if injectVblank == 7:
        rng.advanceRng(seed, 1)
    seed = rng.advanceRng(seed, 1)
    # You thought variable number of RNG calls was weird, now we're doing a
    # random number between 0 and a random number.
    bottom7 = rng.top(seed) % (rand + 1)
    bottom7 += 0x1E
    # Again the game does a & 07F, but again we don't lose information.
    halfword |= bottom7
    # bit 6 is now the random number from the top or'ed with this new random
    # number (which is not likely to be 1). It's...uhh...weird. And I don't
    # fully understand why it's needed.
    return (seed, halfword)
Exemple #4
0
def GoodFrames():
    """Use This to find locally good item grabs."""
    seed = 0x8E0222DD
    partySize = 6
    framesToSearch = 4800
    steps = 0

    while steps < framesToSearch:
        thisSeed = rng.advanceRng(seed, steps)
        pickups, advSteps = PickupReturn(thisSeed, partySize)
        candies = pickups.get('Rare Candy', 0)
        if candies > 1:
            print('MULTI-CANDY: Advance %d "steps" and get %s' %
                  (steps, pickups))
        elif candies == 1:
            if ('Ultra Ball' in pickups or 'Nugget' in pickups
                    or 'Protein' in pickups or 'PP Up' in pickups):
                print('CANDY+: Advance %d "steps" and get %s' %
                      (steps, pickups))
        if advSteps > 0:
            steps += advSteps
        else:
            steps += 1
Exemple #5
0
def searchAt(seed, index1, list2, index2):
    fidMatches = set()
    # Advance 1 for SID, and 1 more for frame advance
    seed = rng.advanceRng(seed, 2)
    # The first candidate is always generated with no problem with VBlank
    seed, firstCandidate = fid.generateCandidateFID(seed)

    # Starting with the second candidate FID, things get chaotic due to VBlank.
    # Within the 2nd word, the trendy phrase can be changed based on where the
    # VBlank occurs.  If it doesn't, it may affect which FID is generated.
    # This can all affect the number of RNG advances during the 2nd FID, which
    # means we have different possibilities to check for subsequent FIDs as
    # well.
    for inject in range(9):
        nextSeed, candidate = fid.generateCandidateFID(seed, inject)
        candidates = [firstCandidate, candidate]
        # 3 more FIDs to generate.
        for i in range(3):
            nextSeed, candidate = fid.generateCandidateFID(nextSeed)
            candidates.append(candidate)
        candidates.sort()
        if candidateMatches(candidates[0], index1, list2, index2):
            fidMatches.add(candidates[0].FID)
    return fidMatches
Exemple #6
0
def generateCandidateFID(seed, injectVblank=-1):
    """Generates a full CandidateFID at the given seed, injecting an extra
    RNG advancement at the index given by injectVblank.
    
    Args:
        seed: the starting seed to generate from.
        injectVblank: optional.  Where to inject an extra rng advancement.
          Valid values are 0 to 8 inclusive."""
    if injectVblank == 0:
        seed = rng.advanceRng(seed, 1)
    seed, firstTrendyWord = getTrendyWord(seed, 0xA)

    if injectVblank == 1:
        seed = rng.advanceRng(seed, 1)
    seed = rng.advanceRng(seed, 1)
    nextIndex = 0xD if rng.top(seed) & 1 == 0 else 0xC
    
    if injectVblank == 2:
        seed = rng.advanceRng(seed, 1)
    seed, secondTrendyWord = getTrendyWord(seed, nextIndex)

    if injectVblank == 3:
        seed = rng.advanceRng(seed, 1)
    seed, comparator = getComparator(seed, injectVblank)
    
    if injectVblank == 8:
        seed = rng.advanceRng(seed, 1)
    seed = rng.advanceRng(seed, 1)
    FID = rng.top(seed)
    
    # print("\t%x" % firstHalfWord)
    # print("\tFID: %x" % FID)
    # print("\t%x" % thirdHalfWord)
    # print("\t%x" % fourthHalfWord)
    
    return seed, Candidate(comparator, FID, firstTrendyWord, secondTrendyWord)
Exemple #7
0
def main():
    seed = 0x6529CF57
    seed = rng.advanceRng(seed, 77)
    seed = rng.advanceRng(seed, 2)  # Either 1 or 2
    saveSeed = seed
    generateFID(seed)
Exemple #8
0
def main():
    """This does an A* search to find a good battle/menuing chain to get to
    TO_FIND items. It should reutrn something close to optimal, but to keep
    things efficient, the heuristic can be outdone by superb RNG."""
    # Best g seen for each inventory state.  Used to not explore nodes that we
    # know can't be good.
    bestSeen = {}
    pq = []
    heapq.heappush(pq, Node({}, 0, 0, []))
    # TODO: insert first node
    while len(pq) > 0:
        node = heapq.heappop(pq)
        if node.heuristic() == 0:
            print("DONE!")
            print(node.items)
            print(node.actions)
            print("Expected advances: %d" % node.advances)
            break
        # If there are any items on the party, consider removing them
        if node.partyItems > 0:
            newItems = {}
            copyItems(node.items, newItems)
            newActions = []
            for action in node.actions:
                newActions.append(action)
            newActions.append(Action('M', menuFrames(node.partyItems)))
            heapq.heappush(
                pq,
                Node(newItems, 0, node.advances + menuFrames(node.partyItems),
                     newActions))

        # Start searching for candies after at least 1 battle
        seed = rng.advanceRng(INITIAL_SEED,
                              node.advances + LOWEST_RNG_ADVANCES_PER_BATTLE)

        party = MAX_PARTY_SIZE - node.partyItems
        # Assuming that if we wait this long, we might as well have gotten
        # another item in between.
        framesToSearch = 4 * LOWEST_RNG_ADVANCES_PER_BATTLE

        steps = 0
        while steps < framesToSearch:
            thisSeed = rng.advanceRng(seed, steps)
            pickups, advSteps = PickupReturn(thisSeed, party)
            oldSteps = steps
            if advSteps > 0:
                steps += advSteps
            else:
                steps += 1

            numTotalPickups = 0
            numUsefulPickups = 0
            newItems = {}
            for item in [
                    'Rare Candy', 'Protein', 'Ultra Ball', 'PP Up', 'Nugget'
            ]:
                numTotalPickups += pickups.get(item, 0)
                newItems[item] = node.items.get(item, 0) + pickups.get(item, 0)
                if node.items.get(item, 0) < TO_FIND.get(item, 0):
                    numUsefulPickups += pickups.get(item, 0)
            # We didn't find anything good, so don't bother recording this action.
            if numUsefulPickups == 0:
                continue
            # Don't forget to count useless pickups because it still affects party size!
            numTotalPickups += pickups.get('useless', 0)

            newActions = []
            for action in node.actions:
                newActions.append(action)
            newActions.append(Action('B', oldSteps))
            newPartyItems = node.partyItems + numTotalPickups
            newAdvances = node.advances + LOWEST_RNG_ADVANCES_PER_BATTLE + oldSteps + 90

            newPartySize = party - numTotalPickups
            if newPartySize < 0:
                newPartySize = 0
                print('uh oh')
            if newPartySize == 0:
                # The +90 is for fade out/in on the battle.  I need to structure
                # this better to get rid of that...
                newActions.append(Action('M', menuFrames(MAX_PARTY_SIZE)))
                newAdvances += menuFrames(MAX_PARTY_SIZE)
                newPartyItems = 0

            # Check if this new node has already been explored at the same or
            # earlier frame.
            inv = hashableInventory(newItems, newPartyItems)
            seen = bestSeen.get(inv, BIG_NUMBER)
            if newAdvances >= seen:
                # Note that if we're equal we also abandon this state. Something
                # else has already investigated from this position (or better) so
                # we don't need to contine.
                continue
            bestSeen[inv] = newAdvances

            heapq.heappush(
                pq, Node(newItems, newPartyItems, newAdvances, newActions))