예제 #1
0
def buildBossString(reward, world):
    text = ''
    for location in world.get_locations():
        if location.item.name == reward:
            text += '\x08' + get_raw_text(getHint(location.name).text)
    return text
예제 #2
0
def buildWorldGossipHints(spoiler, world, checkedLocations=None):
    # rebuild hint exclusion list
    hintExclusions(world, clear_cache=True)

    world.barren_dungeon = False
    world.woth_dungeon = 0

    search = Search.max_explore([w.state for w in spoiler.worlds])
    for stone in gossipLocations.values():
        stone.reachable = (search.spot_access(
            world.get_location(stone.location))
                           and search.state_list[world.id].guarantee_hint())

    if checkedLocations is None:
        checkedLocations = set()

    stoneIDs = list(gossipLocations.keys())

    world.distribution.configure_gossip(spoiler, stoneIDs)

    random.shuffle(stoneIDs)

    hint_dist = hint_dist_sets[world.hint_dist]
    hint_types, hint_prob = zip(*hint_dist.items())
    hint_prob, _ = zip(*hint_prob)

    # Add required location hints
    alwaysLocations = getHintGroup('always', world)
    for hint in alwaysLocations:
        location = world.get_location(hint.name)
        checkedLocations.add(hint.name)

        location_text = getHint(location.name, world.clearer_hints).text
        if '#' not in location_text:
            location_text = '#%s#' % location_text
        item_text = getHint(getItemGenericName(location.item),
                            world.clearer_hints).text
        add_hint(spoiler,
                 world,
                 stoneIDs,
                 GossipText('%s #%s#.' % (location_text, item_text),
                            ['Green', 'Red']),
                 hint_dist['always'][1],
                 location,
                 force_reachable=True)

    # Add trial hints
    if world.trials_random and world.trials == 6:
        add_hint(spoiler,
                 world,
                 stoneIDs,
                 GossipText(
                     "#Ganon's Tower# is protected by a powerful barrier.",
                     ['Pink']),
                 hint_dist['trial'][1],
                 force_reachable=True)
    elif world.trials_random and world.trials == 0:
        add_hint(spoiler,
                 world,
                 stoneIDs,
                 GossipText(
                     "Sheik dispelled the barrier around #Ganon's Tower#.",
                     ['Yellow']),
                 hint_dist['trial'][1],
                 force_reachable=True)
    elif world.trials < 6 and world.trials > 3:
        for trial, skipped in world.skipped_trials.items():
            if skipped:
                add_hint(spoiler,
                         world,
                         stoneIDs,
                         GossipText(
                             "the #%s Trial# was dispelled by Sheik." % trial,
                             ['Yellow']),
                         hint_dist['trial'][1],
                         force_reachable=True)
    elif world.trials <= 3 and world.trials > 0:
        for trial, skipped in world.skipped_trials.items():
            if not skipped:
                add_hint(spoiler,
                         world,
                         stoneIDs,
                         GossipText(
                             "the #%s Trial# protects Ganon's Tower." % trial,
                             ['Pink']),
                         hint_dist['trial'][1],
                         force_reachable=True)

    hint_types = list(hint_types)
    hint_prob = list(hint_prob)
    hint_counts = {}

    if world.hint_dist == "tournament":
        fixed_hint_types = []
        for hint_type in hint_types:
            fixed_hint_types.extend([hint_type] * int(hint_dist[hint_type][0]))
        fill_hint_types = ['sometimes', 'random']
        current_fill_type = fill_hint_types.pop(0)

    while stoneIDs:
        if world.hint_dist == "tournament":
            if fixed_hint_types:
                hint_type = fixed_hint_types.pop(0)
            else:
                hint_type = current_fill_type
        else:
            try:
                # Weight the probabilities such that hints that are over the expected proportion
                # will be drawn less, and hints that are under will be drawn more.
                # This tightens the variance quite a bit. The variance can be adjusted via the power
                weighted_hint_prob = []
                for w1_type, w1_prob in zip(hint_types, hint_prob):
                    p = w1_prob
                    if p != 0:  # If the base prob is 0, then it's 0
                        for w2_type, w2_prob in zip(hint_types, hint_prob):
                            if w2_prob != 0:  # If the other prob is 0, then it has no effect
                                # Raising this term to a power greater than 1 will decrease variance
                                # Conversely, a power less than 1 will increase variance
                                p = p * (
                                    ((hint_counts.get(w2_type, 0) / w2_prob) +
                                     1) /
                                    ((hint_counts.get(w1_type, 0) / w1_prob) +
                                     1))
                    weighted_hint_prob.append(p)

                hint_type = random_choices(hint_types,
                                           weights=weighted_hint_prob)[0]
            except IndexError:
                raise Exception(
                    'Not enough valid hints to fill gossip stone locations.')

        hint = hint_func[hint_type](spoiler, world, checkedLocations)

        if hint == None:
            index = hint_types.index(hint_type)
            hint_prob[index] = 0
            if world.hint_dist == "tournament" and hint_type == current_fill_type:
                logging.getLogger('').info(
                    'Not enough valid %s hints for tournament distribution.',
                    hint_type)
                if fill_hint_types:
                    current_fill_type = fill_hint_types.pop(0)
                    logging.getLogger('').info(
                        'Switching to %s hints to fill remaining gossip stone locations.',
                        current_fill_type)
                else:
                    raise Exception(
                        'Not enough valid hints for tournament distribution.')
        else:
            gossip_text, location = hint
            place_ok = add_hint(spoiler, world, stoneIDs, gossip_text,
                                hint_dist[hint_type][1], location)
            if place_ok:
                hint_counts[hint_type] = hint_counts.get(hint_type, 0) + 1
            if not place_ok and world.hint_dist == "tournament":
                logging.getLogger('').debug('Failed to place %s hint for %s.',
                                            hint_type, location.name)
                fixed_hint_types.insert(0, hint_type)
예제 #3
0
def buildGossipHints(world, messages):

    stoneIDs = [
        0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
        0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412,
        0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B,
        0x041C, 0x041D, 0x041E, 0x041F, 0x0420
    ]

    #shuffles the stone addresses for randomization, always locations will be placed first and twice
    random.shuffle(stoneIDs)

    stoneLocations = {
        0x0401: 'Zoras Fountain - Fairy',
        0x0402: 'Zoras Fountain - Jabu',
        0x0403: 'Lake Hylia - Lab',
        0x0404: 'Death Mountain Trail - Biggoron',
        0x0405: 'Death Mountain Crater - Bombable Wall',
        0x0406: 'Temple of Time - Left',
        0x0407: 'Temple of Time - Middle-Left',
        0x0408: 'Lake Hylia - South-West Corner',
        0x0409: 'Zoras Domain - Mweep',
        0x040A: 'Graveyard - Shadow Temple',
        0x040B: 'Hyrule Castle - Rock Wall',
        0x040C: 'Zoras River - Waterfall',
        0x040D: 'Zoras River - Plateau',
        0x040E: 'Temple of Time - Middle-Right',
        0x040F: 'Lake Hylia - South-East Corner',
        0x0410: 'Temple of Time - Right',
        0x0411: 'Gerudo Valley - Waterfall',
        0x0412: 'Hyrule Castle - Malon',
        0x0413: 'Hyrule Castle - Storms Grotto',
        0x0414: 'Dodongos Cavern - Bombable Wall',
        0x0415: 'Goron City - Maze',
        0x0416: 'Sacred Forest Meadow - Maze Lower',
        0x0417: 'Sacred Forest Meadow - Maze Upper',
        0x0418: 'Generic Grotto',
        0x0419: 'Goron City - Medigoron',
        0x041A: 'Desert Colossus - Spirit Temple',
        0x041B: 'Hyrule Field - Hammer Grotto',
        0x041C: 'Sacred Forest Meadow - Saria',
        0x041D: 'Lost Woods - Bridge',
        0x041E: 'Kokiri Forest - Storms',
        0x041F: 'Kokiri Forest - Deku Tree Left',
        0x0420: 'Kokiri Forest - Deku Tree Right'
    }

    spoilerHintsList.append('-Way of the Hero-')
    # add required items locations for hints (good hints)
    requiredSample = []
    requiredSample1 = world.spoiler.required_locations
    #print([(location.parent_region.name, location.item.name) for location in requiredSample])
    # loop through the locations we got for "always required locatiosns"
    # if you find lens, remove it
    for l in requiredSample1:
        if l.item.name in ['Master Sword', 'Stone of Agony']:
            continue
        else:
            requiredSample.append(l)
    #print([(location.parent_region.name, location.item.name) for location in requiredSample])
    if len(requiredSample) >= 4:
        requiredSample = random.sample(requiredSample, 4)  #Pick exactly 4
    for location in requiredSample:
        for _ in range(0, 3):  #and distribute each 3 times (12 / 32)
            ID = stoneIDs.pop(0)
            if location.parent_region.dungeon:
                update_hint(messages, ID, buildHintString(getHint(location.parent_region.dungeon.name).text + \
                    " is on the way of the hero."))
                spoilerHintsList.append(location.parent_region.dungeon.name + ': ' + location.item.name + \
                    ' (' + stoneLocations[ID] + ')')
                #print(location.parent_region.dungeon.name, ': ', location.item.name)
            else:
                update_hint(
                    messages, ID,
                    buildHintString(location.parent_region.name +
                                    " is on the way of the hero."))
                spoilerHintsList.append(location.parent_region.name + ": " + location.item.name + \
                    ' (' + stoneLocations[ID] + ')')
                #print(location.parent_region.name, ': ', location.item.name)
    # Don't repeat hints
    checkedLocations = []

    spoilerHintsList.append('\n-Required Locations-')
    # Add required location hints
    alwaysLocations = getHintGroup('alwaysLocation', world)
    for hint in alwaysLocations:
        for locationWorld in world.get_locations():
            if hint.name == locationWorld.name:
                checkedLocations.append(hint.name)
                for _ in range(0, 2):  #populate each of these twice (24 / 32)
                    ID = stoneIDs.pop(0)
                    update_hint(messages, ID, getHint(locationWorld.name).text + " " + \
                        getHint(getItemGenericName(locationWorld.item)).text + ".")
                    spoilerHintsList.append(locationWorld.name + ": " + locationWorld.item.name + \
                        ' (' + stoneLocations[ID] + ')')

##    spoilerHintsList.append('\n-Good Locations-')
##    # Add good location hints
##    sometimesLocations = getHintGroup('location', world)
##    if sometimesLocations:
##        # for _ in range(0, random.randint(9,10) - len(alwaysLocations)):
##        for _ in range(0, 2): # Exactly 2 of these (26 /
##            hint = random.choice(sometimesLocations)
##            # Repick if location isn't new
##            while hint.name in checkedLocations or hint.name in alwaysLocations:
##                hint = random.choice(sometimesLocations)
##
##            for locationWorld in world.get_locations():
##                if hint.name == locationWorld.name:
##                    checkedLocations.append(locationWorld.name)
##                    update_hint(messages, stoneIDs.pop(0), getHint(locationWorld.name).text + " " + \
##                        getHint(getItemGenericName(locationWorld.item)).text + ".")
##                    spoilerHintsList.append(locationWorld.name + ': ' + locationWorld.item.name)

##    spoilerHintsList.append('\n-BadItem Dungeon-')
##    # add bad dungeon locations hints
##    for dungeon in random.sample(world.dungeons, 2): # Exactly 2 of these (28 / 32)
##        # Choose a randome dungeon location that is a non-dungeon item
##        locationWorld = random.choice([location for region in dungeon.regions for location in world.get_region(region).locations
##            if location.item.type != 'Event' and \
##            not location.name in eventlocations and \
##            not isDungeonItem(location.item) and \
##            (world.tokensanity != 'off' or location.item.name != 'Gold Skulltulla Token') and\
##            location.item.type != 'Song'])
##
##        checkedLocations.append(locationWorld.name)
##        update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(dungeon.name).text + \
##            " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
##        spoilerHintsList.append(dungeon.name + ': ' + locationWorld.item.name)
##
##    spoilerHintsList.append('\n-BadItem Overworld-')
##    # add bad overworld locations hints
##    # only choose location if it is new and a proper item from the overworld
##    overworldlocations = [locationWorld for locationWorld in world.get_locations()
##            if not locationWorld.name in checkedLocations and \
##            not locationWorld.name in alwaysLocations and \
##            not locationWorld.name in sometimesLocations and \
##            locationWorld.item.type != 'Event' and \
##            not locationWorld.name in eventlocations and \
##            (world.tokensanity == 'all' or locationWorld.item.name != 'Gold Skulltulla Token') and \
##            not locationWorld.parent_region.dungeon and \
##            not locationWorld.name in checkedLocations]
##    overworldSample = overworldlocations
##    if len(overworldSample) >= 2: # Only need to check for 2
##        overworldSample = random.sample(overworldlocations, 2) # Exactly 2 of these (30 / 32)
##    for locationWorld in overworldSample:
##        checkedLocations.append(locationWorld.name)
##        update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
##            " can be found at " + locationWorld.parent_region.name + "."))
##        spoilerHintsList.append(locationWorld.parent_region.name + ': ' + locationWorld.item.name)

#Populate 4 bad item hints
    spoilerHintsList.append('\n-Bad Items-')
    # add good item hints
    # only choose location if it is new and a good item
    if world.shuffle_weird_egg:
        gooditems.append('Weird Egg')
    baditemlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            not locationWorld.name in alwaysLocations and \
            (world.tokensanity == 'all' or locationWorld.item.name != 'Gold Skulltulla Token') and \
            locationWorld.item.type != 'Event' and \
            not locationWorld.name in eventlocations and \
            not isDungeonItem(locationWorld.item) and \
            locationWorld.item.name not in gooditems]
    baditemSample = random.sample(baditemlocations, 4)
    # Don't need this check, we'll fill the rest from this pool, usually only 2, can be more in very rare cases
    # if len(gooditemSample) >= 5:
    #    gooditemSample = random.sample(gooditemlocations, random.randint(3,5))
    # for locationWorld in gooditemSample:
    for locationWorld in baditemSample:
        #locationWorld = gooditemSample.pop()
        checkedLocations.append(locationWorld.name)
        ID = stoneIDs.pop(0)
        if locationWorld.parent_region.dungeon:
            update_hint(messages, ID, buildHintString(getHint(locationWorld.parent_region.dungeon.name).text + \
                " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
            spoilerHintsList.append(locationWorld.parent_region.dungeon.name + ': ' + locationWorld.item.name + \
                ' (' + stoneLocations[ID] + ')')
        else:
            update_hint(messages, ID, buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
                " can be found at " + locationWorld.parent_region.name + "."))
            spoilerHintsList.append(locationWorld.parent_region.name + ': ' + locationWorld.item.name + \
                ' (' + stoneLocations[ID] + ')')

    spoilerHintsList.append('\n-Good Items-')
    # add good item hints
    # only choose location if it is new and a good item
    if world.shuffle_weird_egg:
        gooditems.append('Weird Egg')
    gooditemlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            locationWorld.item.name in gooditems]
    gooditemSample = gooditemlocations
    # Don't need this check, we'll fill the rest from this pool, usually only 2, can be more in very rare cases
    # if len(gooditemSample) >= 5:
    #    gooditemSample = random.sample(gooditemlocations, random.randint(3,5))
    # for locationWorld in gooditemSample:
    random.shuffle(gooditemSample)
    while stoneIDs:
        locationWorld = gooditemSample.pop()
        checkedLocations.append(locationWorld.name)
        ID = stoneIDs.pop(0)
        if locationWorld.parent_region.dungeon:
            update_hint(messages, ID, buildHintString(getHint(locationWorld.parent_region.dungeon.name).text + \
                " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
            spoilerHintsList.append(locationWorld.parent_region.dungeon.name + ': ' + locationWorld.item.name + \
                ' (' + stoneLocations[ID] + ')')
        else:
            update_hint(messages, ID, buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
                " can be found at " + locationWorld.parent_region.name + "."))
            spoilerHintsList.append(locationWorld.parent_region.name + ': ' + locationWorld.item.name + \
                ' (' + stoneLocations[ID] + ')')

    #spoilerHintsList.append('\n-Junk-\n')

    f = open("hints.txt", "w")
    f.write('~~~ NEW HINTS ~~~\n\n')
    f.write('\n'.join(spoilerHintsList))
    f.close()
예제 #4
0
def buildGossipHints(world):

    stoneIDs = [
        0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
        0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412,
        0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B,
        0x041C, 0x041D, 0x041E, 0x041F, 0x0420
    ]

    #shuffles the stone addresses for randomization, always locations will be placed first
    random.shuffle(stoneIDs)

    # Add trial hints
    if world.trials < 6 and world.trials > 3:
        for trial, skipped in world.skipped_trials.items():
            if skipped:
                add_hint(
                    world, stoneIDs.pop(0),
                    buildHintString("the " +
                                    colorText(trial + " Trial", 'Yellow') +
                                    " was dispelled by Sheik."))
    elif world.trials <= 3 and world.trials > 0:
        for trial, skipped in world.skipped_trials.items():
            if not skipped:
                add_hint(
                    world, stoneIDs.pop(0),
                    buildHintString("the " +
                                    colorText(trial + " Trial", 'Pink') +
                                    " protects Ganon's Tower."))

    # add required items locations for hints (good hints)
    requiredSample = world.spoiler.required_locations
    if len(requiredSample) >= 5:
        requiredSample = random.sample(requiredSample, random.randint(3, 4))
    for location in requiredSample:
        if location.parent_region.dungeon:
            add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(location.parent_region.dungeon.name, world).text, 'Light Blue') + \
                " is on the way of the hero."))
        else:
            add_hint(
                world, stoneIDs.pop(0),
                buildHintString(
                    colorText(location.hint, 'Light Blue') +
                    " is on the way of the hero."))

    # Don't repeat hints
    checkedLocations = []

    # Add required location hints
    alwaysLocations = getHintGroup('alwaysLocation', world)
    for hint in alwaysLocations:
        for locationWorld in world.get_locations():
            if hint.name == locationWorld.name:
                checkedLocations.append(hint.name)
                add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(locationWorld.name, world).text, 'Green') + " " + \
                    colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + "."))

    # Add good location hints
    sometimesLocations = getHintGroup('location', world)
    if sometimesLocations:
        for _ in range(0, random.randint(11, 12) - len(alwaysLocations)):
            hint = random.choice(sometimesLocations)
            # Repick if location isn't new
            while hint.name in checkedLocations or hint.name in alwaysLocations:
                hint = random.choice(sometimesLocations)

            for locationWorld in world.get_locations():
                if hint.name == locationWorld.name:
                    checkedLocations.append(locationWorld.name)
                    add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(locationWorld.name, world).text, 'Green') + " " + \
                        colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + "."))

    # add bad dungeon locations hints
    for dungeon in random.sample(world.dungeons, random.randint(3, 4)):
        # Choose a randome dungeon location that is a non-dungeon item
        locationWorld = random.choice([location for region in dungeon.regions for location in region.locations
            if location.item.type != 'Event' and \
            location.item.type != 'Shop' and \
            not location.event and \
            not isDungeonItem(location.item) and \
            (world.tokensanity != 'off' or location.item.name != 'Gold Skulltulla Token') and\
            location.item.type != 'Song'])

        checkedLocations.append(locationWorld.name)
        add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(dungeon.name, world).text, 'Green') + \
            " hoards " + colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + "."))

    # add bad overworld locations hints
    # only choose location if it is new and a proper item from the overworld
    overworldlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            not locationWorld.name in alwaysLocations and \
            not locationWorld.name in sometimesLocations and \
            locationWorld.item.type != 'Event' and \
            locationWorld.item.type != 'Shop' and \
            not locationWorld.event and \
            (world.tokensanity == 'all' or locationWorld.item.name != 'Gold Skulltulla Token') and \
            not locationWorld.parent_region.dungeon]
    overworldSample = overworldlocations
    if len(overworldSample) >= 3:
        # Use this hint type to balance hints given via trials
        if world.trials == 3:
            overworldSample = random.sample(overworldlocations,
                                            random.randint(1, 2))
        elif world.trials in [2, 4]:
            overworldSample = random.sample(overworldlocations,
                                            random.randint(1, 3))
        elif world.trials in [1, 5]:
            overworldSample = random.sample(overworldlocations,
                                            random.randint(2, 3))
        else:
            overworldSample = random.sample(overworldlocations,
                                            random.randint(2, 4))
    for locationWorld in overworldSample:
        checkedLocations.append(locationWorld.name)
        add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + \
            " can be found at " + colorText(locationWorld.hint, 'Green') + "."))

    # add good item hints
    # only choose location if it is new and a good item
    gooditemlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            locationWorld.item.advancement and \
            locationWorld.item.type != 'Event' and \
            locationWorld.item.type != 'Shop' and \
            not locationWorld.event and \
            locationWorld.item.name != 'Gold Skulltulla Token' and \
            not locationWorld.item.key]
    gooditemSample = gooditemlocations
    if len(gooditemSample) >= 6:
        gooditemSample = random.sample(gooditemlocations, random.randint(4, 6))
    for locationWorld in gooditemSample:
        checkedLocations.append(locationWorld.name)
        if locationWorld.parent_region.dungeon:
            add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(locationWorld.parent_region.dungeon.name, world).text, 'Green') + \
                " hoards " + colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + "."))
        else:
            add_hint(world, stoneIDs.pop(0), buildHintString(colorText(getHint(getItemGenericName(locationWorld.item), world).text, 'Red') + \
                " can be found at " + colorText(locationWorld.hint, 'Green') + "."))

    # fill the remaining hints with junk
    junkHints = getHintGroup('junkHint', world)
    random.shuffle(junkHints)
    while stoneIDs:
        add_hint(world, stoneIDs.pop(0), junkHints.pop().text)
예제 #5
0
def buildGossipHints(spoiler, world):
    # rebuild hint exclusion list
    hintExclusions(world, clear_cache=True)

    world.barren_dungeon = False

    max_states = State.get_states_with_items([w.state for w in spoiler.worlds],
                                             [])
    for id, stone in gossipLocations.items():
        stone.reachable = \
            max_states[world.id].can_reach(stone.location, resolution_hint='Location') and \
            max_states[world.id].guarantee_hint()

    checkedLocations = []

    stoneIDs = list(gossipLocations.keys())

    world.distribution.configure_gossip(spoiler, stoneIDs)

    random.shuffle(stoneIDs)

    hint_dist = hint_dist_sets[world.hint_dist]
    hint_types, hint_prob = zip(*hint_dist.items())
    hint_prob, hint_count = zip(*hint_prob)

    # Add required location hints
    alwaysLocations = getHintGroup('alwaysLocation', world)
    for hint in alwaysLocations:
        location = world.get_location(hint.name)
        checkedLocations.append(hint.name)

    location_text = getHint(location.name, world.clearer_hints).text
    if '#' not in location_text:
        location_text = '#%s#' % location_text
    item_text = getHint(getItemGenericName(location.item),
                        world.clearer_hints).text
    add_hint(spoiler,
             world,
             stoneIDs,
             GossipText('%s #%s#.' % (location_text, item_text),
                        ['Green', 'Red']),
             hint_dist['always'][1],
             location,
             force_reachable=True)

    # Add trial hints
    if world.trials_random and world.trials == 6:
        add_hint(spoiler,
                 world,
                 stoneIDs,
                 GossipText(
                     "#Ganon's Tower# is protected by a powerful barrier.",
                     ['Pink']),
                 hint_dist['trial'][1],
                 force_reachable=True)
    elif world.trials_random and world.trials == 0:
        add_hint(spoiler,
                 world,
                 stoneIDs,
                 GossipText(
                     "Sheik dispelled the barrier around #Ganon's Tower#.",
                     ['Yellow']),
                 hint_dist['trial'][1],
                 force_reachable=True)
    elif world.trials < 6 and world.trials > 3:
        for trial, skipped in world.skipped_trials.items():
            if skipped:
                add_hint(spoiler,
                         world,
                         stoneIDs,
                         GossipText(
                             "the #%s Trial#  was dispelled by Sheik." % trial,
                             ['Yellow']),
                         hint_dist['trial'][1],
                         force_reachable=True)
    elif world.trials <= 3 and world.trials > 0:
        for trial, skipped in world.skipped_trials.items():
            if not skipped:
                add_hint(spoiler,
                         world,
                         stoneIDs,
                         GossipText(
                             "the #%s Trial# protects Ganon's Tower." % trial,
                             ['Pink']),
                         hint_dist['trial'][1],
                         force_reachable=True)

    hint_types = list(hint_types)
    hint_prob = list(hint_prob)
    if world.hint_dist == "tournament":
        fixed_hint_types = []
        for hint_type in hint_types:
            fixed_hint_types.extend([hint_type] * int(hint_dist[hint_type][0]))

    while stoneIDs:
        if world.hint_dist == "tournament":
            if fixed_hint_types:
                hint_type = fixed_hint_types.pop(0)
            else:
                hint_type = 'loc'
        else:
            try:
                [hint_type] = random_choices(hint_types, weights=hint_prob)
            except IndexError:
                raise Exception(
                    'Not enough valid hints to fill gossip stone locations.')

        hint = hint_func[hint_type](spoiler, world, checkedLocations)

        if hint == None:
            index = hint_types.index(hint_type)
            hint_prob[index] = 0
        else:
            gossip_text, location = hint
            place_ok = add_hint(spoiler, world, stoneIDs, gossip_text,
                                hint_dist[hint_type][1], location)
            if not place_ok and world.hint_dist == "tournament":
                fixed_hint_types.insert(0, hint_type)
예제 #6
0
def buildWorldGossipHints(spoiler, world, checkedLocations=None):
    # rebuild hint exclusion list
    hintExclusions(world, clear_cache=True)

    world.barren_dungeon = 0
    world.woth_dungeon = 0

    search = Search.max_explore([w.state for w in spoiler.worlds])
    for stone in gossipLocations.values():
        stone.reachable = (search.spot_access(
            world.get_location(stone.location))
                           and search.state_list[world.id].guarantee_hint())

    if checkedLocations is None:
        checkedLocations = set()

    stoneIDs = list(gossipLocations.keys())

    world.distribution.configure_gossip(spoiler, stoneIDs)

    if 'disabled' in world.hint_dist_user:
        for stone_name in world.hint_dist_user['disabled']:
            try:
                stone_id = gossipLocations_reversemap[stone_name]
            except KeyError:
                raise ValueError(
                    f'Gossip stone location "{stone_name}" is not valid')
            stoneIDs.remove(stone_id)
            (gossip_text, _) = get_junk_hint(spoiler, world, checkedLocations)
            spoiler.hints[world.id][stone_id] = gossip_text

    stoneGroups = []
    if 'groups' in world.hint_dist_user:
        for group_names in world.hint_dist_user['groups']:
            group = []
            for stone_name in group_names:
                try:
                    stone_id = gossipLocations_reversemap[stone_name]
                except KeyError:
                    raise ValueError(
                        f'Gossip stone location "{stone_name}" is not valid')

                stoneIDs.remove(stone_id)
                group.append(stone_id)
            stoneGroups.append(group)
    # put the remaining locations into singleton groups
    stoneGroups.extend([[id] for id in stoneIDs])

    random.shuffle(stoneGroups)

    # Create list of items for which we want hints. If Bingosync URL is supplied, include items specific to that bingo.
    # If not (or if the URL is invalid), use generic bingo hints
    if world.hint_dist == "bingo":
        bingoDefaults = read_json(data_path('Bingo/generic_bingo_hints.json'))
        if world.bingosync_url is not None and world.bingosync_url.startswith(
                "https://bingosync.com/"
        ):  # Verify that user actually entered a bingosync URL
            logger = logging.getLogger('')
            logger.info("Got Bingosync URL. Building board-specific goals.")
            world.item_hints = buildBingoHintList(world.bingosync_url)
        else:
            world.item_hints = bingoDefaults['settings']['item_hints']

        if world.tokensanity in (
                "overworld", "all") and "Suns Song" not in world.item_hints:
            world.item_hints.append("Suns Song")

        if world.shopsanity != "off" and "Progressive Wallet" not in world.item_hints:
            world.item_hints.append("Progressive Wallet")

    # Load hint distro from distribution file or pre-defined settings
    #
    # 'fixed' key is used to mimic the tournament distribution, creating a list of fixed hint types to fill
    # Once the fixed hint type list is exhausted, weighted random choices are taken like all non-tournament sets
    # This diverges from the tournament distribution where leftover stones are filled with sometimes hints (or random if no sometimes locations remain to be hinted)
    sorted_dist = {}
    type_count = 1
    hint_dist = OrderedDict({})
    fixed_hint_types = []
    max_order = 0
    for hint_type in world.hint_dist_user['distribution']:
        if world.hint_dist_user['distribution'][hint_type]['order'] > 0:
            hint_order = int(
                world.hint_dist_user['distribution'][hint_type]['order'])
            sorted_dist[hint_order] = hint_type
            if max_order < hint_order:
                max_order = hint_order
            type_count = type_count + 1
    if (type_count - 1) < max_order:
        raise Exception(
            "There are gaps in the custom hint orders. Please revise your plando file to remove them."
        )
    for i in range(1, type_count):
        hint_type = sorted_dist[i]
        if world.hint_dist_user['distribution'][hint_type]['copies'] > 0:
            fixed_num = world.hint_dist_user['distribution'][hint_type][
                'fixed']
            hint_weight = world.hint_dist_user['distribution'][hint_type][
                'weight']
        else:
            logging.getLogger('').warning(
                "Hint copies is zero for type %s. Assuming this hint type should be disabled.",
                hint_type)
            fixed_num = 0
            hint_weight = 0
        hint_dist[hint_type] = (
            hint_weight,
            world.hint_dist_user['distribution'][hint_type]['copies'])
        hint_dist.move_to_end(hint_type)
        fixed_hint_types.extend([hint_type] * int(fixed_num))

    hint_types, hint_prob = zip(*hint_dist.items())
    hint_prob, _ = zip(*hint_prob)

    # Add required location hints, only if hint copies > 0
    if hint_dist['always'][1] > 0:
        alwaysLocations = getHintGroup('always', world)
        for hint in alwaysLocations:
            location = world.get_location(hint.name)
            checkedLocations.add(hint.name)
            if location.item.name in bingoBottlesForHints and world.hint_dist == 'bingo':
                always_item = 'Bottle'
            else:
                always_item = location.item.name
            if always_item in world.item_hints:
                world.item_hints.remove(always_item)

            if location.name in world.hint_text_overrides:
                location_text = world.hint_text_overrides[location.name]
            else:
                location_text = getHint(location.name,
                                        world.clearer_hints).text
            if '#' not in location_text:
                location_text = '#%s#' % location_text
            item_text = getHint(getItemGenericName(location.item),
                                world.clearer_hints).text
            add_hint(spoiler,
                     world,
                     stoneGroups,
                     GossipText('%s #%s#.' % (location_text, item_text),
                                ['Green', 'Red']),
                     hint_dist['always'][1],
                     location,
                     force_reachable=True)
            logging.getLogger('').debug('Placed always hint for %s.',
                                        location.name)

    # Add trial hints, only if hint copies > 0
    if hint_dist['trial'][1] > 0:
        if world.trials_random and world.trials == 6:
            add_hint(spoiler,
                     world,
                     stoneGroups,
                     GossipText(
                         "#Ganon's Tower# is protected by a powerful barrier.",
                         ['Pink']),
                     hint_dist['trial'][1],
                     force_reachable=True)
        elif world.trials_random and world.trials == 0:
            add_hint(spoiler,
                     world,
                     stoneGroups,
                     GossipText(
                         "Sheik dispelled the barrier around #Ganon's Tower#.",
                         ['Yellow']),
                     hint_dist['trial'][1],
                     force_reachable=True)
        elif world.trials < 6 and world.trials > 3:
            for trial, skipped in world.skipped_trials.items():
                if skipped:
                    add_hint(spoiler,
                             world,
                             stoneGroups,
                             GossipText(
                                 "the #%s Trial# was dispelled by Sheik." %
                                 trial, ['Yellow']),
                             hint_dist['trial'][1],
                             force_reachable=True)
        elif world.trials <= 3 and world.trials > 0:
            for trial, skipped in world.skipped_trials.items():
                if not skipped:
                    add_hint(spoiler,
                             world,
                             stoneGroups,
                             GossipText(
                                 "the #%s Trial# protects Ganon's Tower." %
                                 trial, ['Pink']),
                             hint_dist['trial'][1],
                             force_reachable=True)

    # Add user-specified hinted item locations if using a built-in hint distribution
    # Raise error if hint copies is zero
    if len(world.item_hints
           ) > 0 and world.hint_dist_user['named_items_required']:
        if hint_dist['named-item'][1] == 0:
            raise Exception(
                'User-provided item hints were requested, but copies per named-item hint is zero'
            )
        else:
            for i in range(0, len(world.item_hints)):
                hint = get_specific_item_hint(spoiler, world, checkedLocations)
                if hint == None:
                    raise Exception('No valid hints for user-provided item')
                else:
                    gossip_text, location = hint
                    place_ok = add_hint(spoiler, world, stoneGroups,
                                        gossip_text,
                                        hint_dist['named-item'][1], location)
                    if not place_ok:
                        raise Exception(
                            'Not enough gossip stones for user-provided item hints'
                        )

    hint_types = list(hint_types)
    hint_prob = list(hint_prob)
    hint_counts = {}

    custom_fixed = True
    while stoneGroups:
        if fixed_hint_types:
            hint_type = fixed_hint_types.pop(0)
            copies = hint_dist[hint_type][1]
            if copies > len(stoneGroups):
                # Quiet to avoid leaking information.
                logging.getLogger('').debug(
                    f'Not enough gossip stone locations ({len(stoneGroups)} groups) for fixed hint type {hint_type} with {copies} copies, proceeding with available stones.'
                )
                copies = len(stoneGroups)
        else:
            custom_fixed = False
            # Make sure there are enough stones left for each hint type
            num_types = len(hint_types)
            hint_types = list(
                filter(lambda htype: hint_dist[htype][1] <= len(stoneGroups),
                       hint_types))
            new_num_types = len(hint_types)
            if new_num_types == 0:
                raise Exception(
                    'Not enough gossip stone locations for remaining weighted hint types.'
                )
            elif new_num_types < num_types:
                hint_prob = []
                for htype in hint_types:
                    hint_prob.append(hint_dist[htype][0])
            try:
                # Weight the probabilities such that hints that are over the expected proportion
                # will be drawn less, and hints that are under will be drawn more.
                # This tightens the variance quite a bit. The variance can be adjusted via the power
                weighted_hint_prob = []
                for w1_type, w1_prob in zip(hint_types, hint_prob):
                    p = w1_prob
                    if p != 0:  # If the base prob is 0, then it's 0
                        for w2_type, w2_prob in zip(hint_types, hint_prob):
                            if w2_prob != 0:  # If the other prob is 0, then it has no effect
                                # Raising this term to a power greater than 1 will decrease variance
                                # Conversely, a power less than 1 will increase variance
                                p = p * (
                                    ((hint_counts.get(w2_type, 0) / w2_prob) +
                                     1) /
                                    ((hint_counts.get(w1_type, 0) / w1_prob) +
                                     1))
                    weighted_hint_prob.append(p)

                hint_type = random_choices(hint_types,
                                           weights=weighted_hint_prob)[0]
                copies = hint_dist[hint_type][1]
            except IndexError:
                raise Exception(
                    'Not enough valid hints to fill gossip stone locations.')

        hint = hint_func[hint_type](spoiler, world, checkedLocations)

        if hint == None:
            index = hint_types.index(hint_type)
            hint_prob[index] = 0
            # Zero out the probability in the base distribution in case the probability list is modified
            # to fit hint types in remaining gossip stones
            hint_dist[hint_type] = (0.0, copies)
        else:
            gossip_text, location = hint
            place_ok = add_hint(spoiler, world, stoneGroups, gossip_text,
                                copies, location)
            if place_ok:
                hint_counts[hint_type] = hint_counts.get(hint_type, 0) + 1
                if location is None:
                    logging.getLogger('').debug('Placed %s hint.', hint_type)
                else:
                    logging.getLogger('').debug('Placed %s hint for %s.',
                                                hint_type, location.name)
            if not place_ok and custom_fixed:
                logging.getLogger('').debug(
                    'Failed to place %s fixed hint for %s.', hint_type,
                    location.name)
                fixed_hint_types.insert(0, hint_type)
예제 #7
0
def buildGossipHints(world, messages):

    stoneIDs = [
        0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409,
        0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412,
        0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B,
        0x041C, 0x041D, 0x041E, 0x041F, 0x0420
    ]

    #shuffles the stone addresses for randomization, always locations will be placed first and twice
    random.shuffle(stoneIDs)

    spoilerHintsList.append('-Way of the Hero-')
    # add required items locations for hints (good hints)
    requiredSample = []
    requiredSample1 = world.spoiler.required_locations
    #print([(location.parent_region.name, location.item.name) for location in requiredSample])
    # loop through the locations we got for "always required locatiosns"
    # if you find lens, remove it
    for l in requiredSample1:
        if l.item.name in ['Master Sword', 'Stone of Agony']:
            continue
        else:
            requiredSample.append(l)
    #print([(location.parent_region.name, location.item.name) for location in requiredSample])
    if len(requiredSample) >= 4:
        requiredSample = random.sample(requiredSample, 4)  #Pick exactly 4
    for location in requiredSample:
        for _ in range(0, 3):  #and distribute each 3 times (12 / 32)
            if location.parent_region.dungeon:
                update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(location.parent_region.dungeon.name).text + \
                    " is on the way of the hero."))
                spoilerHintsList.append(location.parent_region.dungeon.name +
                                        ': ' + location.item.name)
                #print(location.parent_region.dungeon.name, ': ', location.item.name)
            else:
                update_hint(
                    messages, stoneIDs.pop(0),
                    buildHintString(location.parent_region.name +
                                    " is on the way of the hero."))
                spoilerHintsList.append(location.parent_region.name + ": " +
                                        location.item.name)
                #print(location.parent_region.name, ': ', location.item.name)
    # Don't repeat hints
    checkedLocations = []

    spoilerHintsList.append('\n-Required Locations-')
    # Add required location hints
    alwaysLocations = getHintGroup('alwaysLocation', world)
    for hint in alwaysLocations:
        for locationWorld in world.get_locations():
            if hint.name == locationWorld.name:
                checkedLocations.append(hint.name)
                for _ in range(0, 2):  #populate each of these twice (24 / 32)
                    update_hint(messages, stoneIDs.pop(0), getHint(locationWorld.name).text + " " + \
                        getHint(getItemGenericName(locationWorld.item)).text + ".")
                    spoilerHintsList.append(locationWorld.name + ": " +
                                            locationWorld.item.name)

##    spoilerHintsList.append('\n-Good Locations-')
##    # Add good location hints
##    sometimesLocations = getHintGroup('location', world)
##    if sometimesLocations:
##        # for _ in range(0, random.randint(9,10) - len(alwaysLocations)):
##        for _ in range(0, 2): # Exactly 2 of these (26 /
##            hint = random.choice(sometimesLocations)
##            # Repick if location isn't new
##            while hint.name in checkedLocations or hint.name in alwaysLocations:
##                hint = random.choice(sometimesLocations)
##
##            for locationWorld in world.get_locations():
##                if hint.name == locationWorld.name:
##                    checkedLocations.append(locationWorld.name)
##                    update_hint(messages, stoneIDs.pop(0), getHint(locationWorld.name).text + " " + \
##                        getHint(getItemGenericName(locationWorld.item)).text + ".")
##                    spoilerHintsList.append(locationWorld.name + ': ' + locationWorld.item.name)

##    spoilerHintsList.append('\n-BadItem Dungeon-')
##    # add bad dungeon locations hints
##    for dungeon in random.sample(world.dungeons, 2): # Exactly 2 of these (28 / 32)
##        # Choose a randome dungeon location that is a non-dungeon item
##        locationWorld = random.choice([location for region in dungeon.regions for location in world.get_region(region).locations
##            if location.item.type != 'Event' and \
##            not location.name in eventlocations and \
##            not isDungeonItem(location.item) and \
##            (world.tokensanity != 'off' or location.item.name != 'Gold Skulltulla Token') and\
##            location.item.type != 'Song'])
##
##        checkedLocations.append(locationWorld.name)
##        update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(dungeon.name).text + \
##            " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
##        spoilerHintsList.append(dungeon.name + ': ' + locationWorld.item.name)
##
##    spoilerHintsList.append('\n-BadItem Overworld-')
##    # add bad overworld locations hints
##    # only choose location if it is new and a proper item from the overworld
##    overworldlocations = [locationWorld for locationWorld in world.get_locations()
##            if not locationWorld.name in checkedLocations and \
##            not locationWorld.name in alwaysLocations and \
##            not locationWorld.name in sometimesLocations and \
##            locationWorld.item.type != 'Event' and \
##            not locationWorld.name in eventlocations and \
##            (world.tokensanity == 'all' or locationWorld.item.name != 'Gold Skulltulla Token') and \
##            not locationWorld.parent_region.dungeon and \
##            not locationWorld.name in checkedLocations]
##    overworldSample = overworldlocations
##    if len(overworldSample) >= 2: # Only need to check for 2
##        overworldSample = random.sample(overworldlocations, 2) # Exactly 2 of these (30 / 32)
##    for locationWorld in overworldSample:
##        checkedLocations.append(locationWorld.name)
##        update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
##            " can be found at " + locationWorld.parent_region.name + "."))
##        spoilerHintsList.append(locationWorld.parent_region.name + ': ' + locationWorld.item.name)

#Populate 4 bad item hints
    spoilerHintsList.append('\n-Bad Items-')
    # add good item hints
    # only choose location if it is new and a good item
    if world.shuffle_weird_egg:
        gooditems.append('Weird Egg')
    baditemlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            not locationWorld.name in alwaysLocations and \
            (world.tokensanity == 'all' or locationWorld.item.name != 'Gold Skulltulla Token') and \
            locationWorld.item.type != 'Event' and \
            not locationWorld.name in eventlocations and \
            not isDungeonItem(locationWorld.item) and \
            locationWorld.item.name not in gooditems]
    baditemSample = random.sample(baditemlocations, 4)
    # Don't need this check, we'll fill the rest from this pool, usually only 2, can be more in very rare cases
    # if len(gooditemSample) >= 5:
    #    gooditemSample = random.sample(gooditemlocations, random.randint(3,5))
    # for locationWorld in gooditemSample:
    for locationWorld in baditemSample:
        #locationWorld = gooditemSample.pop()
        checkedLocations.append(locationWorld.name)
        if locationWorld.parent_region.dungeon:
            update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(locationWorld.parent_region.dungeon.name).text + \
                " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
            spoilerHintsList.append(locationWorld.parent_region.dungeon.name +
                                    ': ' + locationWorld.item.name)
        else:
            update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
                " can be found at " + locationWorld.parent_region.name + "."))
            spoilerHintsList.append(locationWorld.parent_region.name + ': ' +
                                    locationWorld.item.name)

    spoilerHintsList.append('\n-Good Items-')
    # add good item hints
    # only choose location if it is new and a good item
    if world.shuffle_weird_egg:
        gooditems.append('Weird Egg')
    gooditemlocations = [locationWorld for locationWorld in world.get_locations()
            if not locationWorld.name in checkedLocations and \
            locationWorld.item.name in gooditems]
    gooditemSample = gooditemlocations
    # Don't need this check, we'll fill the rest from this pool, usually only 2, can be more in very rare cases
    # if len(gooditemSample) >= 5:
    #    gooditemSample = random.sample(gooditemlocations, random.randint(3,5))
    # for locationWorld in gooditemSample:
    random.shuffle(gooditemSample)
    while stoneIDs:
        locationWorld = gooditemSample.pop()
        checkedLocations.append(locationWorld.name)
        if locationWorld.parent_region.dungeon:
            update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(locationWorld.parent_region.dungeon.name).text + \
                " hoards " + getHint(getItemGenericName(locationWorld.item)).text + "."))
            spoilerHintsList.append(locationWorld.parent_region.dungeon.name +
                                    ': ' + locationWorld.item.name)
        else:
            update_hint(messages, stoneIDs.pop(0), buildHintString(getHint(getItemGenericName(locationWorld.item)).text + \
                " can be found at " + locationWorld.parent_region.name + "."))
            spoilerHintsList.append(locationWorld.parent_region.name + ': ' +
                                    locationWorld.item.name)

    #spoilerHintsList.append('\n-Junk-\n')

    f = open("hints.txt", "w")
    f.write('~~~ NEW HINTS ~~~\n\n')
    f.write('\n'.join(spoilerHintsList))
    f.close()
예제 #8
0
def buildGossipHints(world, rom):
    stoneAddresses = [
        0x938e4c, 0x938EA8, 0x938F04, 0x938F60, 0x938FBC, 0x939018, 0x939074,
        0x9390D0, 0x93912C, 0x939188, 0x9391E4, 0x939240, 0x93929C, 0x9392F8,
        0x939354, 0x9393B0, 0x93940C, 0x939468, 0x9394C4, 0x939520, 0x93957C,
        0x9395D8, 0x939634, 0x939690, 0x9396EC, 0x939748, 0x9397A4, 0x939800,
        0x93985C, 0x9398B8, 0x939914, 0x939970
    ]  #address for gossip stone text boxes, byte limit is 92

    alwaysLocations = getHintGroup(
        'alwaysLocation'
    )  #These location will always have a hint somewhere in the world.

    sometimesSpace = (int(
        (len(stoneAddresses) - len(alwaysLocations) * 2) / 2))
    sometimesLocations = getHintGroup(
        'location'
    )  #A random selection of these locations will be in the hint pool.
    random.shuffle(sometimesLocations)
    sometimesLocations = sometimesLocations[0:sometimesSpace]
    hintList = alwaysLocations
    hintList.extend(alwaysLocations)
    hintList.extend(sometimesLocations)

    locationData = []
    for hint in hintList:
        for locationWorld in world.get_locations():
            if hint.name == locationWorld.name:
                locationData.extend([locationWorld])

    #hopefully fixes weird VC error where the last character from a previous text box would sometimes spill over into the next box.
    for address in range(stoneAddresses[0], 0x9399D8):
        rom.write_byte(address, 0x08)

    #shuffles the stone addresses for randomization, always locations will be placed first and twice
    random.shuffle(stoneAddresses)

    #loops through shuffled locations and addresses and builds hint.
    while locationData:
        currentLoc = locationData.pop(0)
        Block_code = getBytes((getHint(currentLoc.name).text))
        if currentLoc.item.type == 'Map' or currentLoc.item.type == 'Compass' or currentLoc.item.type == 'BossKey' or currentLoc.item.type == 'SmallKey':
            Block_code.extend(getBytes((getHint(currentLoc.item.type).text)))
        else:
            Block_code.extend(getBytes((getHint(currentLoc.item.name).text)))
        endText(Block_code)

        if len(Block_code) > 92:
            print('Too many characters in hint')
            Block_code = getBytes("I am Error.")
            Block_code.extend(getBytes(currentLoc.name))
            Block_code.extend(getBytes('&'))
            Block_code.extend(getBytes(currentLoc.item.name))

        rom.write_bytes(stoneAddresses.pop(0), Block_code)

    junkHints = getHintGroup('junkHint')
    random.shuffle(junkHints)
    while stoneAddresses:
        junkHint = junkHints.pop()
        Block_code = getBytes(junkHint.text)
        endText(Block_code)
        rom.write_bytes(stoneAddresses.pop(0), Block_code)

    return rom