def test_levels(self):
        #same orientation
        orientation = 'tall_wide'
        block1 = Block(block_mesh, orientation, (0, 0, 0))
        block2 = Block(block_mesh, orientation, (2, 1, 0))

        print(block1.get_top_level())
        print(block2.get_top_level())
        self.assertTrue(block1.get_top_level() == block2.get_top_level())

        print(block1.get_bottom_level())
        print(block2.get_bottom_level())
        self.assertTrue(block1.get_bottom_level() == block2.get_bottom_level())

        # differing orientations
        block1 = Block(block_mesh, 'flat_wide', ( 0,  0, 0))
        block2 = Block(block_mesh, 'tall_thin', ( 5, 3, -7))

        print(block1.get_top_level())
        print(block2.get_top_level())
        self.assertTrue(block1.get_top_level() == block2.get_top_level())

        print(block1.get_bottom_level())
        print(block2.get_bottom_level())
        self.assertTrue(block1.get_bottom_level() > block2.get_bottom_level())
Beispiel #2
0
def is_stable(tower_state, new_block: Block):
    """
    Boolean recursive function that calculates if the new block to be places in the
    scheme of the current block state will stand, or topple.
    :param tower_state: A state of the block tower.
    :param new_block: potential block to add, organized in a dictionary with keys as levels and values list of blocks
                    level -> [block1, block2, ...]
    :return: True iff the new arrangement still stands
    """
    bottom_level = new_block.get_bottom_level()
    top_level = new_block.get_top_level()
    # blocks_by_top_level= tower_state.get_blocks_by_top_level()
    # blocks_by_bottom_level= tower_state.get_blocks_by_bottom_level()

    # Initiate the relation to surrounding blocks in the tower
    # Short-circuit the expensive calculation if can be skipped.
    blocks_below = calculate_below(new_block, tower_state.get_by_top(bottom_level - 1)) \
        if (bottom_level - 1) in tower_state \
        else set()
    tower_state.set_blocks_below(new_block, blocks_below)

    blocks_above = calculate_above(new_block, tower_state.get_by_bottom(top_level + 1)) \
        if (top_level + 1) in tower_state \
        else set()
    tower_state.set_blocks_above(new_block, blocks_above)

    # Last attempt to find if this situation was already stored as a unstable combination
    if tower_state.is_bad_block(
            tower_state.stringify_block_neighbors(new_block)):
        return False

    # connect the new block to the blocks above and below by making changes to their neighbor setting.
    tower_state.connect_block_to_neighbors(new_block)

    if is_stable_helper((tower_state, new_block)):
        return True
    else:
        # We can stringify this block's failure, contingent on it's neighbors.
        # This will allow to quickly check in the future if this block is stable, relative to its surroundings
        tower_state.add_bad_block_state(new_block)

        # release the relation of this block on it neighbors
        tower_state.disconnect_block_from_neighbors(new_block)
        return False
Beispiel #3
0
def is_overlapping(block_tower, new_block: Block):
    """
    Check if new block clashes (physically overlaps) with the rest of the block tower
    :param block_tower: A tower state
    :param new_block: either a new object or just it's string representation
    :return:
    """
    if block_tower.is_bad_block(new_block):
        return True

    bottom_level: int = new_block.get_bottom_level()
    top_level: int = new_block.get_top_level()
    """
    Only scan blocks with top levels above my bottom level.
    X - Block. B - Bottom layer. T - Top layer
    
                        XXXXXXX          
    new block           XXXXXXX         others  TTTTTTTT     others
                        BBBBBBB           in    XXXXXXXX       out
                                                                        TTTT
                                                                        XXXX
    
    """
    for level in filter(lambda l: l >= bottom_level, block_tower.keys()):
        for other_block in block_tower.get_by_top(level):
            """
            Diqualify for overlap any block with a bottom above our top
            X - Block. B - Bottom layer. T - Top layer

                                            others  XXXXXXXX      others    XXXXX
                                              in    XXXXXXXX       out      BBBBB
                                TTTTTTT             BBBBBBBB
            new block           XXXXXXX               
                                XXXXXXX           


            """
            if other_block.get_bottom_level(
            ) <= top_level and other_block.is_overlapping(new_block):
                # perform a memoization of bad blocks we've seen
                block_tower.add_bad_block(new_block)
                return True
    return False