예제 #1
0
    def cleanup(self):
        growth = CharGrowthObject.get(self.index)
        for attr in ["power", "stamina", "magic", "hit",
                     "evade", "mdef"]:
            baseattr = "%s_base" % attr
            increase = getattr(growth, attr) * (self.level-1) / 100
            initial = getattr(self, baseattr) + increase
            setattr(self, attr, initial)

        hpgroup = HPGrowthObject.getgroup(self.index)
        mpgroup = MPGrowthObject.getgroup(self.index)
        max_hp = mutate_normal(85, minimum=1, maximum=999,
                               random_degree=self.random_degree)
        max_mp = mutate_normal(9, minimum=1, maximum=99,
                               random_degree=self.random_degree)
        temp_level = 1
        while temp_level < self.level:
            temp_level += 1
            hpo = [o for o in hpgroup if temp_level <= o.level][-1]
            mpo = [o for o in mpgroup if temp_level <= o.level][-1]
            max_hp += hpo.increase
            max_mp += mpo.increase
        max_hp = min(999, max(max_hp, 1))
        max_mp = min(99, max(max_mp, 1))
        self.max_hp, self.hp = max_hp, max_hp
        self.max_mp, self.mp = max_mp, max_mp

        if self.level > 1:
            self.xp = sum([e.experience for e in ExperienceObject
                           if e.index < (self.level-1)])
        else:
            self.xp = 0
        self.xpnext = ExperienceObject.get(self.level-1).experience

        for attr in ["helmet", "armor"]:
            current = getattr(self, attr)
            if self.can_equip(current):
                continue
            candidates = [c for c in ItemObject.every_equippable
                          if getattr(c, "is_%s" % attr) and
                          self.can_equip(c) and
                          c.buyable]
            if not candidates:
                candidates = [c for c in ItemObject.every_equippable
                              if getattr(c, "is_%s" % attr) and
                              c.buyable]
            chosen = min(candidates, key=lambda c: c.rank_price)
            chosen.make_equippable(self.index)
            setattr(self, attr, chosen.full_index)
        current = ItemObject.get(self.accessory)
        if not self.can_equip(current):
            candidates = [c for c in Accessory2Object.every
                          if self.can_equip(c)]
            if candidates:
                chosen = random.choice(candidates)
                chosen.make_equippable(self.index)
                self.accessory = chosen.full_index
예제 #2
0
    def mutate_stat_curve(cls, class_index, attr):
        lus = [
            lu for lu in LevelUpObject.every if lu.class_index == class_index
        ]
        assert len(lus) == 99
        lus = lus[:98]
        assert len(lus) == 98

        lus[0].reseed(salt="fullmut" + attr)
        bits = [lu.get_bit(attr) for lu in lus]
        value = len([b for b in bits if b])
        base_ratio = value / float(len(lus))
        max_ratio = max([
            cls.get_class_stat_score(i, attr) / float(len(lus))
            for i in xrange(6)
        ])
        assert max_ratio >= base_ratio
        base_ratio = mutate_normal(base_ratio,
                                   0,
                                   max_ratio,
                                   wide=False,
                                   random_degree=LevelUpObject.random_degree,
                                   return_float=True)
        remaining = list(lus)
        while remaining:
            ratio = mutate_normal(base_ratio,
                                  0,
                                  max_ratio,
                                  wide=False,
                                  random_degree=LevelUpObject.random_degree,
                                  return_float=True)
            max_index = len(remaining) - 1
            divider = random.randint(0, max_index)
            aa = remaining[:divider]
            bb = remaining[divider:]

            if len(aa) > len(bb):
                aa, bb = bb, aa
            elif len(aa) == len(bb) and random.choice([True, False]):
                aa, bb = bb, aa
            if random.choice([True, True, False]):
                to_set, remaining = aa, bb
            else:
                to_set, remaining = bb, aa

            assert len(to_set + remaining) == max_index + 1
            for lu in to_set:
                value = (random.random() < ratio)
                lu.set_bit(attr, value)
예제 #3
0
    def randomize_all(cls):
        meats = {m.meat.old_data['meat'] for m in MonsterObject.every}
        for meat in sorted(meats):
            monsters = [m for m in MonsterObject.every
                        if m.meat.old_data['meat'] == meat
                        and m.index <= MonsterObject.MAX_EVOLVE_INDEX]
            if not monsters:
                assert meat >= 0xc0
                continue
            assert len(monsters) == 5
            assert monsters[-1].level == 0xb
            assert monsters == sorted(monsters, key=lambda m: m.level)
            new_levels = set([])
            for m in monsters[:4]:
                new_level = mutate_normal(
                    m.level, minimum=1, maximum=0xa, wide=True,
                    random_degree=MonsterLevelObject.random_degree)
                new_levels.add(new_level)

            while len(new_levels) < 4:
                new_levels.add(random.randint(1, 0xa))
            new_levels = sorted(new_levels)

            for level, monster in zip(new_levels, monsters):
                assert 1 <= level <= 0xa
                MonsterLevelObject.get(monster.index).set_level(level)

        super(MonsterLevelObject, cls).randomize_all()
예제 #4
0
 def mutate(self):
     item = None
     if self.contents & 0x8000:
         value = (self.contents & 0x7FFF) << 1
     elif self.contents & 0xFF00:
         return
     else:
         item = ItemObject.get(self.contents & 0xff)
         if item is None:
             return
         if item.rare:
             new_item = item.get_similar()
             self.contents = new_item.full_index
             return
         elif not item.buyable:
             return
         value = item.rank_price
     if random.randint(1, 20) == 20:
         value = min(65000, max(0, value))
         value = mutate_normal(value, minimum=0, maximum=65000,
                               random_degree=self.random_degree)
         self.contents = 0x8000 | (value >> 1)
         return
     if item is None:
         item = [i for i in ItemObject.every_buyable
                 if i.rank_price <= value][-1]
     new_item = item.get_similar()
     self.contents = new_item.full_index
예제 #5
0
    def randomize(self):
        num_items = len(self.items)
        num_items = mutate_normal(num_items, minimum=1, maximum=8,
                                  random_degree=self.random_degree,
                                  wide=True)
        old_items = list(self.items)
        new_items = []
        for _ in range(500):
            if len(new_items) == num_items:
                break
            buyable_check = not (random.random() < self.random_degree)
            chosen_class = random.choice(old_items).shop_item_class
            chosen_price = random.choice(old_items)
            candidates = [a for a in AttributeObject.every
                          if a.index <= 0x7f and a.rank >= 0
                          and a.shop_item_class == chosen_class
                          and a.is_buyable >= buyable_check]
            candidates = [c for c in candidates if c not in new_items]
            if not candidates:
                continue
            chosen = chosen_price.get_similar(
                    candidates=candidates, random_degree=self.random_degree,
                    override_outsider=True)
            new_items.append(chosen)
        else:
            raise Exception('Unable to populate shop.')

        for i in new_items:
            if not i.is_buyable:
                ItemPriceObject.get(i.index).price = 60000

        self.item_indexes = [i.index for i in new_items]
예제 #6
0
 def mutate(self):
     item = None
     if self.contents & 0x8000:
         value = (self.contents & 0x7FFF) << 1
     elif self.contents & 0xFF00:
         return
     else:
         item = ItemObject.get(self.contents & 0xff)
         if item is None:
             return
         if item.rare:
             new_item = item.get_similar()
             self.contents = new_item.full_index
             return
         elif not item.buyable:
             return
         value = item.rank_price
     if random.randint(1, 20) == 20:
         value = min(65000, max(0, value))
         value = mutate_normal(value,
                               minimum=0,
                               maximum=65000,
                               random_degree=self.random_degree)
         self.contents = 0x8000 | (value >> 1)
         return
     if item is None:
         item = [
             i for i in ItemObject.every_buyable if i.rank_price <= value
         ][-1]
     new_item = item.get_similar()
     self.contents = new_item.full_index
예제 #7
0
    def mutate_spell_levels(cls, class_index):
        lus = [
            lu for lu in LevelUpObject.every if lu.class_index == class_index
        ]
        indexes = [
            i for (i, lu) in enumerate(lus) if lu.get_bit("spell_level")
        ]
        if not indexes:
            return

        new_indexes = []
        for i in indexes:
            while True:
                n = mutate_normal(i, 1, 40, random_degree=cls.random_degree)
                if n not in new_indexes:
                    new_indexes.append(n)
                    break

        if len(new_indexes) > 1:
            remaining = 8 - len(new_indexes)
            highest = max(new_indexes)
            new_indexes.extend(random.sample(range(highest + 1, 50),
                                             remaining))
            assert len(new_indexes) == 8

        assert len(new_indexes) in [1, 8]
        for i, lu in enumerate(lus):
            if i in new_indexes:
                lu.set_bit("spell_level", True)
            else:
                lu.set_bit("spell_level", False)
예제 #8
0
 def mutate(self):
     if self.misc in [2, 4] and self.contents < 0x10:
         return
     partner = random.choice(FF2ChestObject.every)
     assert self.misc in [1, 2, 4]
     assert partner.misc in [1, 2, 4]
     if partner.misc == 1:
         if self.misc == 1:
             value = self.contents
         else:
             value = ff2_get_price(self.contents)
         self.misc = 1
         value = min(65000, max(0, value))
         self.contents = mutate_normal(value,
                                       0,
                                       65000,
                                       wide=True,
                                       random_degree=self.random_degree)
     else:
         if self.misc == 1:
             item = ff2_get_item_valued(self.contents)
             self.misc = 2
         else:
             item = self.contents
         item = ff2_get_similar_item(item, random_degree=self.random_degree)
         self.contents = item
     assert self.misc in [1, 2, 4]
예제 #9
0
 def get_similar_all_items(self):
     candidates = self.sorted_items_and_equipment
     candidates = [c for c in candidates if c.rank > 0]
     index = candidates.index(self)
     max_index = len(candidates) - 1
     new_index = mutate_normal(index,
                               0,
                               max_index,
                               random_degree=self.random_degree)
     return candidates[new_index]
예제 #10
0
 def mutate(self):
     super(DropObject, self).mutate()
     self.item = ItemObject.get(self.item).get_similar().full_index
     self.charm = ItemObject.get(self.charm).get_similar().full_index
     if self.monster.get_bit("bosslike"):
         self.xp = min(self.xp, self.old_data['xp'])
         self.xp = mutate_normal(self.xp, 0, self.xp, wide=True,
                                 random_degree=self.random_degree)
     else:
         self.xp = max(self.xp, self.old_data['xp'])
     self.gp = self.gp >> 1
예제 #11
0
 def mutate(self):
     for (i, c) in enumerate(self.counts):
         subcounts = (c >> 4, c & 0xf)
         final_counts = []
         for sc in subcounts:
             if sc >= 2:
                 sc = mutate_normal(sc, 1, 9, wide=True,
                                    random_degree=self.random_degree)
             final_counts.append(sc)
         low, high = sorted(final_counts)
         self.counts[i] = (low << 4) | high
예제 #12
0
 def get_similar(self):
     if self.rare:
         result = random.choice(self.every_rare)
     elif self.buyable:
         buyables = self.every_buyable
         index = buyables.index(self)
         index = mutate_normal(index, minimum=0, maximum=len(buyables)-1,
                               random_degree=self.random_degree)
         result = buyables[index]
     else:
         result = self
     return result
예제 #13
0
 def mutate(self):
     value = mutate_normal(self.price, minimum=0, maximum=65000,
                           random_degree=self.random_degree)
     value = value * 2
     power = 0
     while value > 100:
         value /= 10
         power += 1
     value = (value * (10**power)) / 2
     self.price = value
     if self.equippable and not self.is_weapon:
         self.equippable = random.randint(1, 127) << 1
예제 #14
0
def ff2_get_similar_item(item, candidates=None, random_degree=None):
    candidates = ff2_get_ranked_items(candidates)
    if random_degree is None:
        random_degree = FF2ChestObject.random_degree
    index = candidates.index(item)
    max_index = len(candidates) - 1
    new_index = mutate_normal(index,
                              0,
                              max_index,
                              wide=True,
                              random_degree=random_degree)
    return candidates[new_index]
예제 #15
0
    def randomize(self):
        if not self.intershuffle_valid:
            return

        other = self.get_similar()
        power = mutate_normal(other.power, minimum=0, maximum=0xf,
                              wide=True)
        if random.random() < (self.random_degree / 2):
            power = int(round(power * 2 / 3))
            count_statuses = bin(self.boost_statuses)
            while bin(self.boost_statuses).count('1') == count_statuses:
                new_status = random.choice([0x10, 0x40, 0x80])
                self.misc_value |= new_status
예제 #16
0
 def get_similar(self):
     if self.rare:
         result = random.choice(self.every_rare)
     elif self.buyable:
         buyables = self.every_buyable
         index = buyables.index(self)
         index = mutate_normal(index,
                               minimum=0,
                               maximum=len(buyables) - 1,
                               random_degree=self.random_degree)
         result = buyables[index]
     else:
         result = self
     return result
예제 #17
0
 def mutate(self):
     super(DropObject, self).mutate()
     self.item = ItemObject.get(self.item).get_similar().full_index
     self.charm = ItemObject.get(self.charm).get_similar().full_index
     if self.monster.get_bit("bosslike"):
         self.xp = min(self.xp, self.old_data['xp'])
         self.xp = mutate_normal(self.xp,
                                 0,
                                 self.xp,
                                 wide=True,
                                 random_degree=self.random_degree)
     else:
         self.xp = max(self.xp, self.old_data['xp'])
     self.gp = self.gp >> 1
예제 #18
0
 def mutate(self):
     value = mutate_normal(self.price,
                           minimum=0,
                           maximum=65000,
                           random_degree=self.random_degree)
     value = value * 2
     power = 0
     while value > 100:
         value /= 10
         power += 1
     value = (value * (10**power)) / 2
     self.price = value
     if self.equippable and not self.is_weapon:
         self.equippable = random.randint(1, 127) << 1
예제 #19
0
    def new_family_attributes(self):
        self.family_attributes, self.extended_family_attributes
        if not hasattr(MonsterObject, '_newfamattr'):
            MonsterObject._newfamattr = {}

        if self.family_key in MonsterObject._newfamattr:
            return MonsterObject._newfamattr[self.family_key]

        if self.family_key >= 0xc0:
            MonsterObject._newfamattr[self.family_key] = \
                MonsterObject._famattr[self.family_key]
            return self.new_family_attributes

        num_attributes = len(self.family_attributes)
        min_attributes = 8
        max_attributes = max(
            len(v) for (k, v) in MonsterObject._famattr.items()
            if (k >> 4) <= 0xb)
        num_attributes = mutate_normal(
            num_attributes, minimum=min_attributes, maximum=max_attributes,
            random_degree=self.random_degree, wide=True)

        new_attributes = []
        while len(new_attributes) < num_attributes:
            old_attribute = random.choice(sorted(self.family_attributes))
            if random.random() > self.random_degree:
                # family attributes
                candidates = sorted(self.family_attributes)
            elif random.random() > self.random_degree:
                # extended family attributes
                candidates = sorted(self.extended_family_attributes)
            else:
                # any attributes
                candidates = {a for i in MonsterObject._exfamattr
                              for a in MonsterObject._exfamattr[i] if i < 0xc}
                candidates = sorted(candidates)

            if len(new_attributes) == 0:
                candidates = [c for c in candidates if c.get_bit('use_battle')]

            new_attribute = old_attribute.get_similar(
                candidates=candidates, random_degree=self.random_degree,
                override_outsider=True)
            if new_attribute not in new_attributes:
                new_attributes.append(new_attribute)

        assert any([a.get_bit('use_battle') for a in new_attributes])
        MonsterObject._newfamattr[self.family_key] = sorted(new_attributes)
        return self.new_family_attributes
예제 #20
0
    def mutate(self):
        super(MonsterObject, self).mutate()

        self.reseed(salt="extra")
        drop_partner = self.get_similar()
        if drop_partner.old_data["drop_type"] or not self.is_boss:
            for attr in ["drop_type", "drop_index", "drop_chance"]:
                setattr(self, attr, drop_partner.old_data[attr])
            if self.drop:
                new_item = self.drop.get_similar_all_items()
                self.set_drop(new_item)
                self.drop_chance = mutate_normal(
                    self.drop_chance,
                    0,
                    100,
                    wide=False,
                    random_degree=self.random_degree)

        self.mutate_magic_bits()
예제 #21
0
    def mutate(self):
        if self.rank <= 0 and self.contents:
            return

        self.reseed(salt="mut")
        partner = random.choice([
            c for c in self.every
            if c.rank > 0 and c.item_type > 0 and c.contents > 0
        ])

        value = self.rank
        if value <= 0:
            high = random.randint(0, partner.rank)
            low = random.randint(0, high)
            value = int(
                round((high * self.random_degree) +
                      (low * (1 - self.random_degree))))

        if not partner.get_bit("contains_item"):
            value = random.randint(int(round(value**0.9)), value)
            value = min(value, 65537)
            value = mutate_normal(value,
                                  1,
                                  65537,
                                  random_degree=self.random_degree)
            value = min(value, 65535)
            self.set_money_amount(value)
        else:
            partner = partner.item
            candidates = [c for c in partner.ranked if 0 < c.rank <= value]
            if not candidates:
                candidates = [c for c in partner.ranked if c.rank > 0]
                chosen = candidates[0]
            else:
                chosen = candidates[-1]
            chosen = chosen.get_similar(
                random_degree=ChestObject.random_degree)
            assert chosen.index > 0
            self.set_item(chosen)
예제 #22
0
 def mutate(self):
     if self.shop_type >= 5:
         return
     if self.index == 0x26:
         assert self.shop_type == 4
         assert len(self.wares) == 1
         return
     if self.rank < 0:
         return
     new_wares = []
     candidates = [
         c for c in self.valid_wares if c.old_data["buy_price"] > 2
     ]
     wares = self.wares
     random.shuffle(wares)
     for w in wares:
         while True:
             nw = w.get_similar(candidates,
                                random_degree=self.random_degree)
             if nw in new_wares:
                 nw = random.choice(self.valid_wares)
             if nw not in new_wares:
                 break
         if nw.rank >= 65536:
             price = max(30000, nw.buy_price, nw.sell_price * 2)
             if price < 50000:
                 price *= 2
             price = min(price, 99999)
             price = mutate_normal(price,
                                   0,
                                   99999,
                                   wide=True,
                                   random_degree=nw.random_degree)
             nw.buy_price = price
         new_wares.append(nw)
     self.wares_indexes = sorted([nw.index for nw in new_wares])
예제 #23
0
    def mutate(self):
        super(MonsterObject, self).mutate()

        if 1 <= self.level <= 99:
            new_level = mutate_normal(self.level,
                                      minimum=1,
                                      maximum=99,
                                      random_degree=self.random_degree)
            old_divisibility = divisibility_rank(self.level)
            new_divisibility = divisibility_rank(new_level)
            if new_divisibility < old_divisibility:
                if not self.is_boss:
                    self.level = new_level
                else:
                    difference = float(new_level) / self.level
                    if difference > 1:
                        difference = 1 / difference
                    difference = (difference * (1 - self.random_degree)) + (
                        self.random_degree**2)
                    if random.random() < difference:
                        self.level = new_level
            elif (not self.is_boss and random.random() <
                  (self.random_degree**0.5)):
                self.level = new_level
예제 #24
0
    def cleanup(self):
        growth = CharGrowthObject.get(self.index)
        for attr in ["power", "stamina", "magic", "hit", "evade", "mdef"]:
            baseattr = "%s_base" % attr
            increase = getattr(growth, attr) * (self.level - 1) / 100
            initial = getattr(self, baseattr) + increase
            setattr(self, attr, initial)

        hpgroup = HPGrowthObject.getgroup(self.index)
        mpgroup = MPGrowthObject.getgroup(self.index)
        max_hp = mutate_normal(85,
                               minimum=1,
                               maximum=999,
                               random_degree=self.random_degree)
        max_mp = mutate_normal(9,
                               minimum=1,
                               maximum=99,
                               random_degree=self.random_degree)
        temp_level = 1
        while temp_level < self.level:
            temp_level += 1
            hpo = [o for o in hpgroup if temp_level <= o.level][-1]
            mpo = [o for o in mpgroup if temp_level <= o.level][-1]
            max_hp += hpo.increase
            max_mp += mpo.increase
        max_hp = min(999, max(max_hp, 1))
        max_mp = min(99, max(max_mp, 1))
        self.max_hp, self.hp = max_hp, max_hp
        self.max_mp, self.mp = max_mp, max_mp

        if self.level > 1:
            self.xp = sum([
                e.experience for e in ExperienceObject
                if e.index < (self.level - 1)
            ])
        else:
            self.xp = 0
        self.xpnext = ExperienceObject.get(self.level - 1).experience

        for attr in ["helmet", "armor"]:
            current = getattr(self, attr)
            if self.can_equip(current):
                continue
            candidates = [
                c for c in ItemObject.every_equippable
                if getattr(c, "is_%s" %
                           attr) and self.can_equip(c) and c.buyable
            ]
            if not candidates:
                candidates = [
                    c for c in ItemObject.every_equippable
                    if getattr(c, "is_%s" % attr) and c.buyable
                ]
            chosen = min(candidates, key=lambda c: c.rank_price)
            chosen.make_equippable(self.index)
            setattr(self, attr, chosen.full_index)
        current = ItemObject.get(self.accessory)
        if not self.can_equip(current):
            candidates = [
                c for c in Accessory2Object.every if self.can_equip(c)
            ]
            if candidates:
                chosen = random.choice(candidates)
                chosen.make_equippable(self.index)
                self.accessory = chosen.full_index
예제 #25
0
    def randomize_skills_and_attributes(self):
        if self.index in MonsterObject.banned_monster_indexes:
            return

        old_attributes = [AttributeObject.get(i)
                          for i in self.attribute_indexes]
        new_attributes = [o for o in old_attributes if o.rank < 0
                          and o.index != 0xFF]
        old_attributes = [o for o in old_attributes if o.rank >= 0]
        num_attributes = len({a for a in new_attributes + old_attributes})
        num_attributes = mutate_normal(num_attributes, minimum=1, maximum=8,
                                       random_degree=self.random_degree,
                                       wide=True)

        if not old_attributes:
            return

        while len(new_attributes) < num_attributes:
            if len([a for a in new_attributes
                    if a.get_bit('use_battle')]) == 0:
                candidates = [a for a in self.new_family_attributes
                              if a.get_bit('use_battle')]
            elif len([a for a in new_attributes
                      if a.get_bit('use_battle')]) == 7:
                candidates = [a for a in self.new_family_attributes
                              if not a.get_bit('use_battle')]
            else:
                candidates = self.new_family_attributes
            candidates = [c for c in candidates
                          if c not in new_attributes]
            old_attribute = random.choice(old_attributes)
            if not candidates:
                if (old_attribute in new_attributes
                        and len(new_attributes) >= self.num_attributes):
                    break
                new_attribute = old_attribute
            else:
                new_attribute = old_attribute.get_similar(
                    candidates, random_degree=self.random_degree,
                    override_outsider=True)

            if new_attribute not in new_attributes:
                new_attributes.append(new_attribute)

        if not new_attributes:
            return

        use_battle = [a for a in new_attributes if a.get_bit('use_battle')]
        if len(use_battle) >= 8:
            use_battle = use_battle[:7]
        random.shuffle(use_battle)
        no_use = [a for a in new_attributes if not a.get_bit('use_battle')]
        no_use = sorted(no_use)

        candidates = [m for m in MoveSelectionObject.every
                      if m.num_moves == len(use_battle)]
        assert 1 <= len(use_battle) <= 7
        chosen = random.choice(candidates)
        MonsterLevelObject.get(self.index).set_move_selection_index(
            chosen.index)

        self.attribute_indexes = [a.index for a in use_battle + no_use]
예제 #26
0
    def rank(self):
        if hasattr(self, '_rank'):
            return self._rank

        by_price = sorted(self.every, key=lambda p: (p.price, p.signature))
        by_price = [p for p in by_price if p.price > 0 and p.is_buyable]
        by_drop_rank = sorted(
            self.every, key=lambda p: (p.drop_rank, p.signature))
        by_drop_rank = [p for p in by_drop_rank if p.drop_rank > 0]
        by_shop_day = sorted(
            self.every, key=lambda p: (p.old_shop_availability, p.signature))
        by_shop_day = [p for p in by_shop_day if p.is_quest_buyable]

        for p in self.every:
            ranks = []
            if p in by_price and p.is_buyable:
                ranks.append(by_price.index(p) / float(len(by_price))-1)
            if p in by_drop_rank:
                ranks.append(
                    by_drop_rank.index(p) / float(len(by_drop_rank))-1)
            if ranks:
                p._rank = min(ranks)

        for p in self.every:
            if hasattr(p, '_rank'):
                for ev in p.evolves:
                    if ev:
                        p2 = PinObject.get(ev)
                        if not hasattr(p2, '_rank') or p2._rank < p._rank:
                            value = gen_random_normal() ** 4
                            new_rank = (value * 1.0) + ((1-value) * p._rank)
                            p2._rank = new_rank

        by_price = sorted(self.every, key=lambda p: (p.price, p.signature))
        by_price = [p for p in by_price if p.price > 0]
        for p in self.every:
            if hasattr(p, '_rank'):
                continue

            if p in by_shop_day:
                p._rank = by_shop_day.index(p) / float(len(by_shop_day)-1)
            elif p in by_price:
                p._rank = by_price.index(p) / float(len(by_price)-1)
            else:
                p._rank = random.random()

        sorted_class = sorted(
            self.every, key=lambda p: (-p.pin_class, p._rank, p.signature))
        sorted_noclass = sorted(
            self.every, key=lambda p: (p._rank, p.signature))
        for p in self.every:
            if p.pin_class >= 5:
                rank = sorted_noclass.index(p)
            else:
                value = gen_random_normal()
                rank = ((sorted_class.index(p) * value)
                        + ((1-value) * sorted_noclass.index(p)))
            p._rank = mutate_normal(
                rank, minimum=0, maximum=len(self.every), wide=True,
                random_degree=self.random_degree**2, return_float=True)

        sorted_noclass = sorted(
            self.every, key=lambda p: (p._rank, p.signature))
        for p in self.every:
            p._rank = sorted_noclass.index(p) / float(len(sorted_noclass)-1)

        return self.rank