def move_pieces(self): """ Move pieces to new action F instances to make it fit, if needed. @return: Created action F instances. @rtype: C{list} of L{ActionF} @note: Function may change L{pieces}. """ global townname_serial if len(self.pieces) <= 255: return [] # Trivially correct. # There are too many pieces. number_action_f = (len(self.pieces) + 254) // 255 pow2 = 1 while pow2 < number_action_f: pow2 = pow2 * 2 if pow2 < 255: number_action_f = pow2 heap = [] # Heap of (summed probability, subset-of-pieces) i = 0 while i < number_action_f: # Index 'i' is added to have a unique sorting when lists have equal total probabilities. heapq.heappush(heap, (0, i, [])) i = i + 1 finished_actions = [] # Index 'idx' is added to have a unique sorting when pieces have equal probabilities. rev_pieces = sorted(((p.probability.value, idx, p) for idx, p in enumerate(self.pieces)), reverse=True) for prob, _idx, piece in rev_pieces: while True: sub = heapq.heappop(heap) if len(sub[2]) < 255: break # If a subset already has the max number of parts, consider it finished. finished_actions.append(sub) sub[2].append(piece) sub = (sub[0] + prob, sub[1], sub[2]) heapq.heappush(heap, sub) finished_actions.extend(heap) # To ensure the chances do not get messed up due to one part needing less bits for its # selection, all parts are forced to use the same number of bits. max_prob = max(sub[0] for sub in finished_actions) num_bits = 1 while (1 << num_bits) < max_prob: num_bits = num_bits + 1 # Assign to action F actFs = [] for _prob, _idx, sub in finished_actions: actF_name = expression.Identifier( "**townname #{:d}**".format(townname_serial), None) townname_serial = townname_serial + 1 town_part = TownNamesPart(sub, self.pos) town_part.set_numbits(num_bits) actF = actionF.ActionF(actF_name, None, None, [town_part], self.pos) actFs.append(actF) # Remove pieces of 'sub' from self.pieces counts = len(self.pieces), len(sub) sub_set = set(sub) self.pieces = [ piece for piece in self.pieces if piece not in sub_set ] assert len(self.pieces) == counts[0] - counts[1] self.pieces.append( TownNamesEntryDefinition(actF_name, expression.ConstantNumeric(1), self.pos)) # update self.parts return actFs
def get_action_list(self): return self.actFs + [ actionF.ActionF(self.name, self.id_number, self.style_name, self.parts, self.pos) ]