Пример #1
0
def fit_elements(meta_input, element_list, OPTIONS):

    schematic_bufferX = 3
    #schematic_bufferZ = 4

    # Also, the schematics must be spaced out a certain length as well
    total_schematic_lengthX = sum(s.size[0] for s in element_list)
    sizeX = total_schematic_lengthX + schematic_bufferX * len(element_list) + 2

    sizeZ = element_list[0].size[2]
    sizeZ += 2 * max(len(meta_input.input_domain), len(
        meta_input.output_range)) + 6

    sizeY = max(s.size[1] for s in element_list) + 5

    level = MCSchematic(shape=(sizeX, sizeY, sizeZ))
    box = BoundingBox((0, 0, 0), (sizeX, sizeY, sizeZ))

    cx, cy, cz = 2, 0, 0

    schematic_origins = {}
    for s in element_list:

        schematic_origins[s] = [cx, cy, cz]

        level.copyBlocksFrom(s.schematic, BoundingBox((0, 0, 0), s.size),
                             (cx, cy, cz))

        cx += s.size[0] + schematic_bufferX

    # The schematics contain their own *relative* input locations in a dict of the form
    # input name --> (x, y, z). We wish to make a giant composite list of all the *absolute* locations
    # of the inputs, this time of the form (x, y, z) --> input name. We obtain the absolute
    # location by adding the individual schematic origin (in our coordinate system) with the
    # schematic's relative coordinate.

    absolute_input_locations = {}
    for s in element_list:
        for key, value in s.relative_input_locations.items():
            abs_loc = add_tuples(schematic_origins[s], value)
            absolute_input_locations[abs_loc] = key

    # The z value for all the input locations will be the same. We just need to grab an arbitrary
    # one and use it to define the starting z location for the input rails

    input_z = 5 + absolute_input_locations.keys()[0][2]
    input_rail_z_values = {}
    for i in meta_input.input_domain:
        input_rail_z_values[i] = input_z
        input_z += 2

    absolute_output_locations = {}
    for s in element_list:
        for key, value in s.relative_output_locations.items():
            abs_loc = add_tuples(schematic_origins[s], value)
            absolute_output_locations[abs_loc] = key

    output_z = 4 + absolute_output_locations.keys()[0][2]
    output_rail_z_values = {}
    for i in meta_input.output_range:
        output_rail_z_values[i] = output_z
        output_z += 2

    # Step through the input locations and build backward to to the point where it joins
    # up with the appropriate input rail. The rail will actually be built later.

    for input_loc, input_name in absolute_input_locations.items():

        cx, cy, cz = input_loc[0], input_loc[1], input_loc[2]

        cz += 1

        while (cz < input_rail_z_values[input_name] - 1):
            level.setBlockAt(cx, cy, cz, REDSTONE)
            cz += 1

        level.setBlockAt(cx, cy, cz, REPEATER)

        cz += 1
        level.setBlockAt(cx, cy, cz, WOOL)
        level.setBlockDataAt(cx, cy, cz,
                             meta_input.input_color_key[input_name])

        cy += 1
        level.setBlockAt(cx, cy, cz, REDSTONE)

    # Step through the output locations and build backward to to the point where it joins
    # up with the appropriate output rail. The rail will actually be built later.

    for output_loc, output_name in absolute_output_locations.items():

        if OPTIONS["make_output_rail"] == False:
            continue

        cx, cy, cz = output_loc[0], output_loc[1], output_loc[2]

        cz += 1

        while (cz < output_rail_z_values[output_name] - 1):
            level.setBlockAt(cx, cy, cz, REDSTONE)
            cy -= 1
            level.setBlockAt(cx, cy, cz, WOOL)
            level.setBlockDataAt(cx, cy, cz,
                                 meta_input.output_color_key[output_name])
            cy += 1
            cz += 1

        level.setBlockAt(cx, cy, cz, REPEATER)
        level.setBlockDataAt(cx, cy, cz, REPEATER_TOWARD_POS_Z)

        cy -= 1
        level.setBlockAt(cx, cy, cz, WOOL)
        level.setBlockDataAt(cx, cy, cz,
                             meta_input.output_color_key[output_name])
        cy += 1

        cz += 1
        level.setBlockAt(cx, cy, cz, WOOL)
        level.setBlockDataAt(cx, cy, cz,
                             meta_input.output_color_key[output_name])

        cy += 1
        level.setBlockAt(cx, cy, cz, REDSTONE)

    # Now build the input rails!

    for input_name, input_z_value in input_rail_z_values.items():
        cx, cy, cz = 0, 2, input_z_value
        while cx < sizeX:

            cy -= 1

            if level.blockAt(cx, cy, cz) != REDSTONE:
                level.setBlockAt(cx, cy, cz, WOOL)
                level.setBlockDataAt(cx, cy, cz,
                                     meta_input.input_color_key[input_name])
            level.setBlockAt(cx, cy, cz - 1, WOOL)
            level.setBlockDataAt(cx, cy, cz - 1, WOOL_BLACK)
            level.setBlockAt(cx, cy, cz + 1, WOOL)
            level.setBlockDataAt(cx, cy, cz + 1, WOOL_BLACK)

            cy += 1

            level.setBlockAt(cx, cy, cz, REDSTONE)

            cx += 1

    # We need to put repeaters on the input rail. There are multiple ways to do so. If POSSIBLE,
    # use the gap method. To do this we need to perform the following algorithm to find the
    # gaps inbetween inputs.

    # Find the x values of the 'gaps' between clusters
    # of inputs. These are nice open spots to put repeaters. The following is a 'find the gap'
    # algorithm that ends up with a list of x values where repeaters should be placed (rep_locs)

    # Get the x values of the inputs
    input_x_values = list(x[0] for x in absolute_input_locations.keys())
    input_x_values.sort()

    # Get the differences between each pair of input x values
    diffs = list(input_x_values[i] - input_x_values[i - 1]
                 for i in range(1, len(input_x_values)))

    # Where these differences are more than 2, that means that we had a jump in the x values.
    # Obtain the indexes of the two input x values on either side of the jump
    gap_index_pairs = list(
        (i, i + 1) for i in range(len(diffs)) if diffs[i] > 2)

    # Finally, for each index pair, we want the x value in between the x values at those indices:
    # input_x_values[x] ---------------- (*) --------------- input_x_values[y] for each (x, y) pair in gap_index_pairs
    rep_locs = list(input_x_values[x] +
                    int((input_x_values[y] - input_x_values[x]) / 2)
                    for x, y in gap_index_pairs)

    # Now put repeaters on the input rail

    # We can get away with gap method if the rep_locs are within 15 spaces apart. Otherwise,
    # we'll HAVE to use an ugly-ass method

    rep_loc_spacings = list(rep_locs[i] - rep_locs[i - 1]
                            for i in range(1, len(rep_locs)))

    if len(rep_loc_spacings) > 0:
        rep_loc_max_spacing = max(rep_loc_spacings)
    else:
        # The method fails... just set max spacing to something ridiculous so that it does the other method
        rep_loc_max_spacing = 100

    if rep_loc_max_spacing <= 14:

        print "inputs - gap method"

        # GAP METHOD -- very nice looking, but doesn't work when the input domain is very large
        # because there *won't be any* gaps within 15 units, so it will place no repeaters.

        cy = 2
        for iter_cx in rep_locs:
            for iter_cz in input_rail_z_values.values():
                level.setBlockAt(iter_cx, cy, iter_cz, REPEATER)
                level.setBlockDataAt(iter_cx, cy, iter_cz,
                                     REPEATER_TOWARD_POS_X)

    else:

        print "inputs - method 2"

        # Method 2 -- aesthetically displeasing... has to fudge around the block edges and produces
        # lines of repeaters with 'imperfections' from scooting the necessary ones

        for input_name, input_z_value in input_rail_z_values.items():
            cx, cy, cz = 12, 2, input_z_value
            while cx < sizeX:
                if level.blockAt(cx, cy - 1, cz) == REDSTONE:
                    level.setBlockAt(cx - 2, cy, cz, REPEATER)
                    level.setBlockDataAt(cx - 2, cy, cz, REPEATER_TOWARD_POS_X)
                elif level.blockAt(cx - 1, cy - 1, cz) == REDSTONE:
                    level.setBlockAt(cx + 1, cy, cz, REPEATER)
                    level.setBlockDataAt(cx + 1, cy, cz, REPEATER_TOWARD_POS_X)
                elif level.blockAt(cx + 1, cy - 1, cz) == REDSTONE:
                    level.setBlockAt(cx - 1, cy, cz, REPEATER)
                    level.setBlockDataAt(cx - 1, cy, cz, REPEATER_TOWARD_POS_X)
                else:
                    level.setBlockAt(cx, cy, cz, REPEATER)
                    level.setBlockDataAt(cx, cy, cz, REPEATER_TOWARD_POS_X)
                cx += 12

    if OPTIONS["make_output_rail"]:

        # Now build the output rails!

        outputHeight = absolute_output_locations.keys()[0][1] + 2

        for output_name, output_z_value in output_rail_z_values.items():

            cx, cy, cz = 0, outputHeight, output_z_value
            while cx < sizeX:

                cy -= 1

                if level.blockAt(cx, cy, cz) != REDSTONE:
                    level.setBlockAt(cx, cy, cz, WOOL)
                    level.setBlockDataAt(
                        cx, cy, cz, meta_input.output_color_key[output_name])
                level.setBlockAt(cx, cy, cz - 1, WOOL)
                level.setBlockDataAt(cx, cy, cz - 1, WOOL_BLACK)
                level.setBlockAt(cx, cy, cz + 1, WOOL)
                level.setBlockDataAt(cx, cy, cz + 1, WOOL_BLACK)

                cy += 1

                level.setBlockAt(cx, cy, cz, REDSTONE)

                cx += 1

        # We need to find gaps for the gap method again

        output_x_values = list(x[0] for x in absolute_output_locations.keys())
        output_x_values.sort()

        diffs = list(output_x_values[i] - output_x_values[i - 1]
                     for i in range(1, len(output_x_values)))

        gap_index_pairs = list(
            (i, i + 1) for i in range(len(diffs)) if diffs[i] > 2)

        rep_locs = list(output_x_values[x] +
                        int((output_x_values[y] - output_x_values[x]) / 2)
                        for x, y in gap_index_pairs)

        if len(rep_loc_spacings) > 0:
            rep_loc_max_spacing = max(rep_loc_spacings)
        else:
            # The method fails... just set max spacing to something ridiculous so that it does the other method
            rep_loc_max_spacing = 100

        if rep_loc_max_spacing < 14:

            print "outputs - gap method"

            # GAP METHOD -- very nice looking, but doesn't work when the input domain is very large
            # because there *won't be any* gaps within 15 units, so it will place no repeaters.

            cy = outputHeight
            for iter_cx in rep_locs:
                for iter_cz in output_rail_z_values.values():
                    level.setBlockAt(iter_cx, cy, iter_cz, REPEATER)
                    level.setBlockDataAt(iter_cx, cy, iter_cz,
                                         REPEATER_TOWARD_NEG_X)

        else:

            print "outputs - method 2"

            # Method 2 -- aesthetically displeasing... has to fudge around the block edges and produces
            # lines of repeaters with 'imperfections' from scooting the necessary ones

            for output_name, output_z_value in output_rail_z_values.items():
                cx, cy, cz = 12, outputHeight, output_z_value
                while cx < sizeX:
                    if level.blockAt(cx, cy - 1, cz) == REDSTONE:
                        level.setBlockAt(cx - 2, cy, cz, REPEATER)
                        level.setBlockDataAt(cx - 2, cy, cz,
                                             REPEATER_TOWARD_NEG_X)
                    elif level.blockAt(cx - 1, cy - 1, cz) == REDSTONE:
                        level.setBlockAt(cx + 1, cy, cz, REPEATER)
                        level.setBlockDataAt(cx + 1, cy, cz,
                                             REPEATER_TOWARD_NEG_X)
                    elif level.blockAt(cx + 1, cy - 1, cz) == REDSTONE:
                        level.setBlockAt(cx - 1, cy, cz, REPEATER)
                        level.setBlockDataAt(cx - 1, cy, cz,
                                             REPEATER_TOWARD_NEG_X)
                    else:
                        level.setBlockAt(cx, cy, cz, REPEATER)
                        level.setBlockDataAt(cx, cy, cz, REPEATER_TOWARD_NEG_X)
                    cx += 12

    return level
Пример #2
0
def generate(comb_equation,
             use_input_color_key=None,
             use_output_color_key=None):

    inputs = comb_equation.inputs
    minterms = comb_equation.minterms

    form = form_tall if len(minterms) > 5 else form_short
    formBox = formBox_tall if len(minterms) > 5 else formBox_short
    implicantLimit = 13 if len(minterms) > 5 else 5

    while len(minterms) % implicantLimit != 0:
        minterms.append({})

    numXCopies = int(math.ceil(len(inputs) / 4))
    sizeX = numXCopies * form.Width + 2
    numYCopies = int(math.ceil(len(minterms) / implicantLimit))
    sizeY = numYCopies * form.Height + 3
    sizeZ = form.Length + 1

    #    print sizeX, sizeY, sizeZ

    level = MCSchematic(shape=(sizeX, sizeY, sizeZ))
    box = BoundingBox((0, 0, 0), (sizeX, sizeY, sizeZ))

    # ================================================================================================

    # Paste the schematic the number of times we know we'll need

    pasteX = 0
    for i in range(numXCopies):
        pasteY = 1
        for i in range(numYCopies):
            level.copyBlocksFrom(form, formBox, (pasteX, pasteY, 0))
            pasteY += form.Height
        pasteX += form.Width

    # Fill the bottom plane with a ground

    # level.fillBlocks(BoundingBox((0, 0, 0), (sizeX, 1, sizeZ)), alphaMaterials.BlockofIron)

    # Build X-ways across each row corresponding to each term

    cx = 0
    cy = 2
    cz = 1
    numTerms = 0
    side = CLOSE_SIDE

    relative_input_locations = {}

    for termIndex in range(len(minterms)):

        term = minterms[termIndex]

        cx = 0
        for i in inputs:

            if i in term.keys():
                mat = TORCH if term[i] else REDSTONE
            else:
                mat = AIR

            data = TORCH_POINTING_NEG_Z if (cz == 1) else TORCH_POINTING_POS_Z

            level.setBlockAt(cx, cy, cz, mat)
            level.setBlockDataAt(cx, cy, cz, data)

            if termIndex == 0:
                sx = cx
                sy = cy - 2
                sz = cz + 4
                for iter_sz in [sz, sz + 1, sz + 2, sz + 3]:
                    level.setBlockAt(sx, sy, iter_sz, WOOL)
                    data = WOOL_BLACK if use_input_color_key == None else use_input_color_key[
                        i]
                    level.setBlockDataAt(sx, sy, iter_sz, data)

                relative_input_locations[i] = [sx, sy, sz + 2]

            cx += 2

        # Build the slice of the side scaffolding that goes on this row's height level:
        # -----------------------------------------------------------------------------
        prevCy = cy
        prevCz = cz

        cx = box.width - 2

        if side == CLOSE_SIDE:
            cz -= 1
            cy -= 1
        elif side == FAR_SIDE:
            cz += 1
            cy -= 1

        if len(term) > 0:
            level.setBlockAt(cx, cy, cz, TORCH)
            level.setBlockDataAt(cx, cy, cz, TORCH_POINTING_POS_X)

        cx += 1
        cy -= 1

        if numTerms in [0, 1]:
            level.setBlockAt(cx, cy, cz, DOUBLE_SLAB)
            level.setBlockDataAt(cx, cy, cz, DOUBLE_SLAB_STONE)
        else:
            level.setBlockAt(cx, cy, cz, SLAB)
            level.setBlockDataAt(cx, cy, cz, STONE_SLAB_TOP)

        cy += 1

        level.setBlockAt(cx, cy, cz, REDSTONE)

        if side == CLOSE_SIDE:
            cz += 1
        elif side == FAR_SIDE:
            cz -= 1

        level.setBlockAt(cx, cy, cz, SLAB)
        level.setBlockDataAt(cx, cy, cz, STONE_SLAB_TOP)

        cy += 1

        level.setBlockAt(cx, cy, cz, REDSTONE)

        if side == CLOSE_SIDE:
            currentCloseTowerTopY = cy
            currentCloseTowerTopZ = cz
        elif side == FAR_SIDE:
            currentFarTowerTopY = cy
            currentFarTowerTopZ = cz

        cy = prevCy
        cz = prevCz
        # -----------------------------------------------------------------------------

        # Switch sides

        side = FAR_SIDE if (side == CLOSE_SIDE) else CLOSE_SIDE

        # The z location alternates depending on the side

        if side == CLOSE_SIDE: cz = 1
        if side == FAR_SIDE: cz = 8

        # Keep track of the number of terms

        numTerms += 1

        # JUMP LOGIC
        # Normal case: cy goes up by one, we are working term by term up one paste of the schematic
        # Special case: We have done 13 terms, and need to 'jump' to the next paste of the schematic
        #    This requires some special connecting and bridging.

        # ------------------------------------------------------------------------------------------
        if numTerms == implicantLimit:

            sx = box.width - 1
            sy = currentCloseTowerTopY
            sz = currentCloseTowerTopZ

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_POINTING_POS_Z)

            sy += 1

            for itr_sz in [sz, sz - 1, sz - 2]:
                level.setBlockAt(sx, sy, itr_sz, WOOL)
                level.setBlockDataAt(sx, sy, itr_sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_ON_GROUND)

            sz -= 1

            level.setBlockAt(sx, sy, sz, REDSTONE)

            sz -= 1

            level.setBlockAt(sx, sy, sz, REPEATER)

            # If we are finished with the whole thing, make the lead the exposes
            # The signal to the rest of the world

            if termIndex == len(minterms) - 1:

                sz += 2
                sy += 1

                data = WOOL_BLACK if use_output_color_key == None else use_output_color_key[
                    comb_equation.name]

                for iter_sz in range(sz, sz + 7, 1):
                    level.setBlockAt(sx, sy, iter_sz, WOOL)
                    level.setBlockDataAt(sx, sy, iter_sz, data)
                    sy += 1
                    level.setBlockAt(sx, sy, iter_sz, REDSTONE)
                    sy -= 1

                sz += 7

                level.setBlockAt(sx, sy, sz, WOOL)
                level.setBlockDataAt(sx, sy, sz, data)

                sy += 1

                level.setBlockAt(sx, sy, sz, REPEATER)
                level.setBlockDataAt(sx, sy, sz, REPEATER_TOWARD_POS_Z)

                lead_location = [sx, sy, sz]

            # -----------------------------------------------------

            sx = box.width - 1
            sy = currentFarTowerTopY
            sz = currentFarTowerTopZ

            sz -= 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, 75)
            level.setBlockDataAt(sx, sy, sz, 5)

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz -= 1
            sy += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz += 1

            level.setBlockAt(sx, sy, sz, REDSTONE)

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_ON_GROUND)

            # Now reset the variables for working up the next paste:

            cy += 4
            numTerms = 0
            side = CLOSE_SIDE
            cz = 1

        else:

            cy += 1

#    level.setBlockAt(0, 0, 0, 20)
#    level.setBlockAt(box.width-1, box.height-1, box.length-1, 20)

#    # Flip the entire schematic around to help make the fitting routine more 'sane'
#    # Also adjust location variables (like the locations of the lead and inputs) to
#    # reflect the Z-flip
#
#    level.flipEastWest()
#    lead_location[2] = sizeZ - 1 - lead_location[2]
#    for ril in relative_input_locations.values():
#        ril[2] = sizeZ - 1 - ril[2]

#    level.setBlockAt(*lead_location, blockID = 35)
#    level.setBlockDataAt(*lead_location, newdata = 0)
#
#    for ril in relative_input_locations.values():
#        level.setBlockAt(*ril, blockID = GLASS)

    ret = CombinationalElement(level)
    ret.relative_output_locations = {comb_equation.name: lead_location}
    ret.relative_input_locations = relative_input_locations
    ret.size = (sizeX, sizeY, sizeZ)

    return ret
def generate(comb_equation, use_input_color_key = None, use_output_color_key = None):
    
    inputs = comb_equation.inputs
    minterms = comb_equation.minterms
    
    form = form_tall if len(minterms) > 5 else form_short
    formBox = formBox_tall if len(minterms) > 5 else formBox_short
    implicantLimit = 13 if len(minterms) > 5 else 5
    
    
    while len(minterms) % implicantLimit != 0:
        minterms.append({})

    numXCopies = int(math.ceil(len(inputs) / 4))
    sizeX = numXCopies * form.Width + 2
    numYCopies = int(math.ceil(len(minterms) / implicantLimit))
    sizeY = numYCopies * form.Height + 3
    sizeZ = form.Length + 1

#    print sizeX, sizeY, sizeZ

    level = MCSchematic(shape=(sizeX, sizeY, sizeZ))
    box = BoundingBox((0, 0, 0), (sizeX, sizeY, sizeZ))


    # ================================================================================================


    # Paste the schematic the number of times we know we'll need

    pasteX = 0
    for i in range(numXCopies):
        pasteY = 1
        for i in range(numYCopies):
            level.copyBlocksFrom(form, formBox, (pasteX, pasteY, 0))
            pasteY += form.Height
        pasteX += form.Width


    # Fill the bottom plane with a ground
    
    # level.fillBlocks(BoundingBox((0, 0, 0), (sizeX, 1, sizeZ)), alphaMaterials.BlockofIron)
    

    # Build X-ways across each row corresponding to each term

    cx = 0
    cy = 2
    cz = 1
    numTerms = 0
    side = CLOSE_SIDE
    
    relative_input_locations = {}
    
    for termIndex in range(len(minterms)):
        
        term = minterms[termIndex]
        
        cx = 0
        for i in inputs:

            if i in term.keys():
                mat = TORCH if term[i] else REDSTONE
            else:
                mat = AIR

            data = TORCH_POINTING_NEG_Z if (cz == 1) else TORCH_POINTING_POS_Z

            level.setBlockAt(cx, cy, cz, mat)
            level.setBlockDataAt(cx, cy, cz, data)
            
            if termIndex == 0:
                sx = cx
                sy = cy - 2
                sz = cz + 4
                for iter_sz in [sz, sz+1, sz+2, sz+3]:
                    level.setBlockAt(sx, sy, iter_sz, WOOL)
                    data = WOOL_BLACK if use_input_color_key == None else use_input_color_key[i]
                    level.setBlockDataAt(sx, sy, iter_sz, data)
                    
                relative_input_locations[i] = [sx, sy, sz+2]

            cx += 2


        # Build the slice of the side scaffolding that goes on this row's height level:
        # -----------------------------------------------------------------------------
        prevCy = cy
        prevCz = cz

        cx = box.width - 2

        if side == CLOSE_SIDE:
            cz -= 1
            cy -= 1
        elif side == FAR_SIDE:
            cz += 1
            cy -= 1

        if len(term) > 0:
            level.setBlockAt(cx, cy, cz, TORCH)
            level.setBlockDataAt(cx, cy, cz, TORCH_POINTING_POS_X)

        cx += 1
        cy -= 1

        if numTerms in [0, 1]:
            level.setBlockAt(cx, cy, cz, DOUBLE_SLAB)
            level.setBlockDataAt(cx, cy, cz, DOUBLE_SLAB_STONE)
        else:
            level.setBlockAt(cx, cy, cz, SLAB)
            level.setBlockDataAt(cx, cy, cz, STONE_SLAB_TOP)

        cy += 1

        level.setBlockAt(cx, cy, cz, REDSTONE)

        if side == CLOSE_SIDE:
            cz += 1
        elif side == FAR_SIDE:
            cz -= 1

        level.setBlockAt(cx, cy, cz, SLAB)
        level.setBlockDataAt(cx, cy, cz, STONE_SLAB_TOP)

        cy += 1

        level.setBlockAt(cx, cy, cz, REDSTONE)

        if side == CLOSE_SIDE:
            currentCloseTowerTopY = cy
            currentCloseTowerTopZ = cz
        elif side == FAR_SIDE:
            currentFarTowerTopY = cy
            currentFarTowerTopZ = cz

        cy = prevCy
        cz = prevCz
        # -----------------------------------------------------------------------------


        # Switch sides

        side = FAR_SIDE if (side == CLOSE_SIDE) else CLOSE_SIDE

        # The z location alternates depending on the side

        if side == CLOSE_SIDE: cz = 1
        if side == FAR_SIDE: cz = 8

        # Keep track of the number of terms

        numTerms += 1

        # JUMP LOGIC
        # Normal case: cy goes up by one, we are working term by term up one paste of the schematic
        # Special case: We have done 13 terms, and need to 'jump' to the next paste of the schematic
        #    This requires some special connecting and bridging.

        # ------------------------------------------------------------------------------------------
        if numTerms == implicantLimit:

            sx = box.width - 1
            sy = currentCloseTowerTopY
            sz = currentCloseTowerTopZ

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_POINTING_POS_Z)

            sy += 1

            for itr_sz in [sz, sz-1, sz-2]:
                level.setBlockAt(sx, sy, itr_sz, WOOL)
                level.setBlockDataAt(sx, sy, itr_sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_ON_GROUND)

            sz -= 1

            level.setBlockAt(sx, sy, sz, REDSTONE)

            sz -= 1

            level.setBlockAt(sx, sy, sz, REPEATER)
            
            # If we are finished with the whole thing, make the lead the exposes
            # The signal to the rest of the world
            
            if termIndex == len(minterms) - 1:

                sz += 2
                sy += 1
                
                data = WOOL_BLACK if use_output_color_key == None else use_output_color_key[comb_equation.name]
                
                for iter_sz in range(sz, sz+7, 1):
                    level.setBlockAt(sx, sy, iter_sz, WOOL)
                    level.setBlockDataAt(sx, sy, iter_sz, data)
                    sy += 1
                    level.setBlockAt(sx, sy, iter_sz, REDSTONE)
                    sy -= 1
                
                sz += 7
                
                level.setBlockAt(sx, sy, sz, WOOL)
                level.setBlockDataAt(sx, sy, sz, data)
                
                sy += 1
                
                level.setBlockAt(sx, sy, sz, REPEATER)
                level.setBlockDataAt(sx, sy, sz, REPEATER_TOWARD_POS_Z)
                
                lead_location = [sx, sy, sz]
                
                

            # -----------------------------------------------------

            sx = box.width - 1
            sy = currentFarTowerTopY
            sz = currentFarTowerTopZ

            sz -= 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, 75)
            level.setBlockDataAt(sx, sy, sz, 5)

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz -= 1
            sy += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sz += 1

            level.setBlockAt(sx, sy, sz, REDSTONE)

            sz += 1

            level.setBlockAt(sx, sy, sz, WOOL)
            level.setBlockDataAt(sx, sy, sz, WOOL_BLACK)

            sy += 1

            level.setBlockAt(sx, sy, sz, TORCH)
            level.setBlockDataAt(sx, sy, sz, TORCH_ON_GROUND)
                
                    
            
            # Now reset the variables for working up the next paste:

            cy += 4
            numTerms = 0
            side = CLOSE_SIDE
            cz = 1

        else:

            cy += 1


#    level.setBlockAt(0, 0, 0, 20)
#    level.setBlockAt(box.width-1, box.height-1, box.length-1, 20)
    
#    # Flip the entire schematic around to help make the fitting routine more 'sane'
#    # Also adjust location variables (like the locations of the lead and inputs) to
#    # reflect the Z-flip
#    
#    level.flipEastWest()
#    lead_location[2] = sizeZ - 1 - lead_location[2]
#    for ril in relative_input_locations.values():
#        ril[2] = sizeZ - 1 - ril[2]
    
#    level.setBlockAt(*lead_location, blockID = 35)
#    level.setBlockDataAt(*lead_location, newdata = 0)
#    
#    for ril in relative_input_locations.values():
#        level.setBlockAt(*ril, blockID = GLASS)
    
    ret = CombinationalElement(level)
    ret.relative_output_locations = {comb_equation.name : lead_location}
    ret.relative_input_locations = relative_input_locations
    ret.size = (sizeX, sizeY, sizeZ)
    
    return ret