def transform(self, transforms): """Transforms start, layers using the given transforms.""" layers = self.layers start = self.start # loop through all single-layer transformations to all layers for i, layer in enumerate(layers): a = layer.rows # aka the memory bucket b = layer.rows # aka the current bucket logmsg('transform', 'Transformation buckets before layer %d:' % i) loglines('transform', lambda: self.str_buckets(a, b)) left = transforms for t in transforms: param, cmd = t left = left[1:] # remove this cmd from the remaining cmds if cmd == 'halign': self.halign = param elif cmd == 'valign': self.valign = param elif cmd == '!': # The ! command just updates A to match B a = b else: a, b = self.apply_transform(t, a, b) # do the transform # adjust start pos for 'n' and 'w' commands if cmd == 'n': start = add_points(start, (0, layers[0].height() * (param - 1))) elif cmd == 'w': start = add_points(start, (layers[0].width() * (param - 1), 0)) if cmd in ('halign', 'valign'): logmsg('transform', 'Set %s=%s' % (t[1], t[0])) else: logmsg('transform', 'Buckets after command %s%s:' % t) loglines('transform', lambda: self.str_buckets(a, b)) # we'll return the result in b layers[i].rows = b self.start, self.layers = start, layers return
def get_info(self): """Retrieve various bits of info about the blueprint.""" rowsets = [layer.grid.rows for layer in self.layers] cells = map(lambda r: r.flatten(), rowsets) if len(cells) == 0: raise BlueprintError('No row data in blueprint.') commands = [c.command for c in cells[0]] cmdset = set(commands) # uniques if '' in cmdset: cmdset.remove('') # count the number of occurrences of each command in the blueprint counts = [(c, commands.count(c)) for c in cmdset] counts.sort(key=lambda x: x[1], reverse=True) # look for the manual-mat character anywhere in the commands, # and check that phase=build (the only mode that we'll support # manual material selection with right now) uses_manual_mats = util.is_substring_in_list(':', cmdset) and \ self.build_type == 'build' # make a row of repeating numbers to annotate the blueprint with width = self.layers[0].grid.width # build the blueprint preview return textwrap.dedent(""" Blueprint name: %s Build type: %s Comment: %s Start position: %s Start comment: %s First layer width: %d First layer height: %d Layer count: %d Uses manual material selection: %s Command use counts: %s """).strip() % ( self.name, self.build_type, self.comment or '', add_points(self.start, (1, 1)), self.start_comment or '', width, self.layers[0].grid.height, len(self.layers), uses_manual_mats, ', '.join("%s:%d" % c for c in counts) ) + \ "\nBlueprint preview:\n" + \ '\n'.join( Grid.str_commands(layer.grid.rows, annotate=True) + \ '\n#' + ''.join(layer.onexit) for layer in self.layers )
def get_nearest_plottable_area_from(grid, start): """ Find the nearest plottable area corner from start. Returns coordinate of nearest plottable area corner. """ # if start is out of bounds for grid, expand dimensions of grid if grid.is_out_of_bounds(*start): grid.expand_dimensions(start[0] + 1, start[1] + 1) # check the cell we started in: if it is plottable, it becomes our # starting cheapest_area cell = grid.get_cell(*start) if cell.plottable and cell.area: return start # start with the innermost ring of cells adjacent to start, then # expand outward ring by ring for ring in xrange(1, 1 + max([grid.width, grid.height])): # starting position in this ring (=NW corner cell of ring) pos = add_points(start, (-ring, -ring)) for direction in (Direction(d) for d in ['e', 's', 'w', 'n']): for _ in xrange(0, 2 * ring): pos = add_points(pos, direction.delta()) # step once in direction if grid.is_out_of_bounds(*pos): continue # outside grid bounds corner = grid.get_cell(*pos) if corner.plottable and corner.area: # cell has an area that can be plotted, return it return pos # found no position with an area we can plot return None
if dx == 0: steps = dy # moving on y axis only elif dy == 0: steps = dx # moving on x axis only else: # determine max diagonal steps we can take # in this direction without going too far steps = min([dx, dy]) keycode = ['[' + direction.compass + ']'] jumpkeycode = ['[+' + direction.compass + ']'] move = direction.delta() if not allowjumps or steps < 8 or not allow_overshoot: # render single movement keys keys.extend(keycode * steps) (x1, y1) = add_points((x1, y1), scale_point(move, steps)) allow_overshoot = True else: # use DF's move-by-10-units commands jumps = (steps // 10) leftover = steps % 10 jumpmove = scale_point(move, 10) # backtracking optimization if leftover >= 8: # test if jumping an extra 10-unit step # would put us outside of the bounds of # the blueprint (want to prevent) (xt, yt) = add_points((x1, y1), scale_point(jumpmove, (jumps + 1))) if self.grid.is_out_of_bounds(xt, yt):
def find_largest_area_in_quad(self, x, y, dir1, dir2, bestarea): """ Given the quad starting at (x, y) and formed by dir1 and dir2 (treated as rays with (x, y) as origin), we find the max contiguous-cell distance along dir1, then for each position along dir1, we find the max contiguous-cell distance along dir2. This allows us to find the largest contiguous area constructable by travelling down dir1, then at a right angle along dir2 for each position. Returns the largest area found. """ command = self.grid.get_cell(x, y).command # Get the min/max size that this area may be, based on the command sizebounds = self.buildconfig.get('sizebounds', command) \ or (1, 1000, 1, 1000) # default sizebounds are very large # Get the max width of this area on the axis defined by # pos and dir1 direction, and max width from # the dir2. # width and height are conceptually aligned to an # east(dir1) x south(dir2) quad below. maxwidth = self.grid.count_contiguous_cells(x, y, dir1) maxheight = self.grid.count_contiguous_cells(x, y, dir2) if maxwidth < sizebounds[0]: # constructions narrower than the minimum width for this # command are ineligible to be any larger than 1 wide maxwidth = 1 elif maxwidth > sizebounds[1]: # don't let constructions be wider than allowed maxwidth = sizebounds[1] if maxheight < sizebounds[2]: # constructions shorter than the minimum height for this # command are ineligible to be any larger than 1 tall maxheight = 1 elif maxheight > sizebounds[3]: # don't let constructions be taller than allowed maxheight = sizebounds[3] if maxwidth * maxheight < bestarea.size(): return None # couldn't be larger than the best one yet found if maxheight == 1 and maxwidth == 1: # 1x1 area, just return it return Area((x, y), (x, y)) # (width x 1) sized area bestarea = Area((x, y), add_points((x, y), scale_point(dir1.delta(), maxwidth - 1))) for ydelta in range(1, maxheight): (xt, yt) = add_points((x, y), scale_point(dir2.delta(), ydelta)) height = ydelta + 1 width = self.grid.count_contiguous_cells(xt, yt, dir1) if width > maxwidth: # this row can't be wider than previous rows width = maxwidth elif width < maxwidth: # successive rows can't be wider than this row maxwidth = width if width * height > bestarea.size(): bestarea = Area((x, y), add_points((xt, yt), scale_point(dir1.delta(), width - 1))) else: continue return bestarea
if dx == 0: steps = dy # moving on y axis only elif dy == 0: steps = dx # moving on x axis only else: # determine max diagonal steps we can take # in this direction without going too far steps = min([dx, dy]) keycode = ['[' + direction.compass + ']'] jumpkeycode = ['[+' + direction.compass + ']'] move = direction.delta() if not allowjumps or steps < 8 or not allow_overshoot: # render single movement keys keys.extend(keycode * steps) (x1, y1) = add_points((x1, y1), scale_point(move, steps)) allow_overshoot = True else: # use DF's move-by-10-units commands jumps = (steps // 10) leftover = steps % 10 jumpmove = scale_point(move, 10) # backtracking optimization if leftover >= 8: # test if jumping an extra 10-unit step # would put us outside of the bounds of # the blueprint (want to prevent) (xt, yt) = add_points((x1, y1), scale_point(jumpmove, (jumps + 1)))
def find_largest_area_in_quad(self, x, y, dir1, dir2, bestarea): """ Given the quad starting at (x, y) and formed by dir1 and dir2 (treated as rays with (x, y) as origin), we find the max contiguous-cell distance along dir1, then for each position along dir1, we find the max contiguous-cell distance along dir2. This allows us to find the largest contiguous area constructable by travelling down dir1, then at a right angle along dir2 for each position. Returns the largest area found. """ command = self.grid.get_cell(x, y).command # Get the min/max size that this area may be, based on the command sizebounds = self.buildconfig.get('sizebounds', command) \ or (1, 1000, 1, 1000) # default sizebounds are very large # Get the max width of this area on the axis defined by # pos and dir1 direction, and max width from # the dir2. # width and height are conceptually aligned to an # east(dir1) x south(dir2) quad below. maxwidth = self.grid.count_contiguous_cells(x, y, dir1) maxheight = self.grid.count_contiguous_cells(x, y, dir2) if maxwidth < sizebounds[0]: # constructions narrower than the minimum width for this # command are ineligible to be any larger than 1 wide maxwidth = 1 elif maxwidth > sizebounds[1]: # don't let constructions be wider than allowed maxwidth = sizebounds[1] if maxheight < sizebounds[2]: # constructions shorter than the minimum height for this # command are ineligible to be any larger than 1 tall maxheight = 1 elif maxheight > sizebounds[3]: # don't let constructions be taller than allowed maxheight = sizebounds[3] if maxwidth * maxheight < bestarea.size(): return None # couldn't be larger than the best one yet found if maxheight == 1 and maxwidth == 1: # 1x1 area, just return it return Area((x, y), (x, y)) # (width x 1) sized area bestarea = Area((x, y), add_points( (x, y), scale_point(dir1.delta(), maxwidth - 1) ) ) for ydelta in range(1, maxheight): (xt, yt) = add_points( (x, y), scale_point(dir2.delta(), ydelta) ) height = ydelta + 1 width = self.grid.count_contiguous_cells(xt, yt, dir1) if width > maxwidth: # this row can't be wider than previous rows width = maxwidth elif width < maxwidth: # successive rows can't be wider than this row maxwidth = width if width * height > bestarea.size(): bestarea = Area((x, y), add_points( (xt, yt), scale_point(dir1.delta(), width - 1) ) ) else: continue return bestarea