def testSinglePartition(self): g = RectangleGridPuzzle(3, 4, 'testSinglePartition') Ishape3Vert = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3Vert') Ishape2Vert = MultiBlock([(0, 0), (0, 1)], 'Ishape2Vert') #Ishape3Vert = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3Vert') g.inner_grid[1, 2].set_rule_shape(Ishape2Vert) g.inner_grid[1, 1].set_rule_shape(Ishape3Vert) g.lower_left().is_entrance = True g.upper_right().is_exit = True g.generate_paths() g.load_paths() g.solve() for p in g.partitions: print('Real Partition', p)
def testRotationShapes(self): ''' Put a rotatable TshapeUp in a 3x4 Grid (2x3 Squares) with 2 Singles. SS SS T SS TTT It would not fit normally, but if rotated on it's side it could ''' g = RectangleGridPuzzle(4, 5, 'testRotationShapes') ''' X XXX ''' TshapeUp = MultiBlock([(0, 0), (1, 0), (2, 0), (1, 1)], name='TshapeUp', can_rotate=True) TshapeDown = MultiBlock([(0, 1), (1, 1), (2, 1), (1, 0)], 'TshapeDown', can_rotate=False) # Single0 = MultiBlock([(0, 1)], 'Single0') # Single1 = MultiBlock([(0, 2)], 'Single1') ''' X X''' IshapeHoriz2 = MultiBlock([(0, 0), (1, 0)]) #Ishape3Vert = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3Vert') g.inner_grid[2, 3].set_rule_shape(TshapeUp) g.inner_grid[1, 1].set_rule_shape(TshapeDown) g.inner_grid[0, 3].set_rule_shape(IshapeHoriz2) g.lower_left().is_entrance = True g.upper_right().is_exit = True g.generate_paths() g.load_paths() g.solve() expected_solutions = [[(0, 0), (0, 1), (1, 1), (1, 2), (1, 3), (0, 3), (0, 4), (1, 4), (2, 4), (3, 4)], [(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (2, 1), (2, 2), (2, 3), (3, 3), (3, 4)]] expected_solutions = set(frozenset(p) for p in expected_solutions) #print('expected_solutions:',len(expected_solutions)) actual_solutions = set(frozenset(p) for p in g.solutions) missing_solutions = expected_solutions - actual_solutions extra_solutions = actual_solutions - expected_solutions self.assertTrue( len(missing_solutions) == 0, 'Missing solutions:' + ','.join(str(s) for s in missing_solutions)) self.assertTrue( len(extra_solutions) == 0, 'Extra solutions:' + ','.join(str(s) for s in extra_solutions))
def testTreehouse0(self): # 4x4 Grid with a 3-block "I" shape in the center and one on the left g = RectangleGridPuzzle(6, 6, 'testTreehouse0') Ishape2Vert = MultiBlock([(0, 0), (0, 1)], 'Ishape2Vert') ''' X XXX''' LshapeUpRight = MultiBlock([(0, 0), (1, 0), (2, 0), (2, 1)], name='LshapeUpRight', can_rotate=True) ''' X XXX''' LshapeUpLeft = MultiBlock([(0, 0), (1, 0), (2, 0), (0, 1)], name='LshapeUpLeft', can_rotate=True) g.inner_grid[3, 0].set_rule_shape(Ishape2Vert) g.inner_grid[1, 1].set_rule_shape(LshapeUpRight) g.inner_grid[3, 2].set_rule_shape(LshapeUpLeft) g.inner_grid[4, 0].sun_color = 'purple' g.inner_grid[0, 3].sun_color = 'purple' g.inner_grid[4, 2].sun_color = 'green' g.inner_grid[1, 4].sun_color = 'green' g[2, 0].is_entrance = True g[3, 0].is_entrance = True g[2, 5].is_exit = True g[3, 5].is_exit = True g.finalize() # These took forever to generate. Think carefully before overwriting... overwrite_treehouse_paths = False g.generate_paths(overwrite_treehouse_paths) g.load_paths() g.solve(break_on_first=True) print('g.solutions', g.solutions) #exp_sols=[[(0,0), (0,1), (0,2), (0,3), (1,3), (1,2), (1,1), (1,0), (2,0), (2,1), (2,2), (2,3), (3,3)], [(0,0), (0,1), (0,2), (0,3), (1,3), (2,3), (2,2), (2,1), (2,0), (3,0), (3,1), (3,2), (3,3)], [(0,0), (1,0), (1,1), (1,2), (1,3), (2,3), (2,2), (2,1), (2,0), (3,0), (3,1), (3,2), (3,3)], [(0,0), (1,0), (2,0), (2,1), (2,2), (2,3), (3,3)]] #exp_sols=set(frozenset(p) for p in exp_sols) first_solution = g.solutions[0] self.assertIsNotNone(first_solution)
def test2Ishapes(self): '''c d e f m n o 8 9 a b j k l 4 5 6 7 g h i 0 1 2 3''' # 4x4 Grid with a 3-block "I" shape in the center and one on the left g = RectangleGridPuzzle(4, 4, 'Ishape3test') Ishape3_1 = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3_1') Ishape3_2 = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3_2') g.inner_grid[1, 1].set_rule_shape(Ishape3_1) g.inner_grid[0, 0].set_rule_shape(Ishape3_2) g.lower_left().is_entrance = True g.upper_right().is_exit = True g.finalize() g.generate_paths() g.load_paths() g.solve() print('g.solutions', g.solutions) expected_solutions = [[(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (1, 2), (1, 1), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (3, 3)], [(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (2, 2), (2, 1), (2, 0), (3, 0), (3, 1), (3, 2), (3, 3)], [(0, 0), (1, 0), (1, 1), (1, 2), (1, 3), (2, 3), (2, 2), (2, 1), (2, 0), (3, 0), (3, 1), (3, 2), (3, 3)], [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (3, 3)]] expected_solutions = set(frozenset(p) for p in expected_solutions) #print('expected_solutions:',len(expected_solutions)) actual_sols = set(frozenset(p) for p in g.solutions) missing_solutions = expected_solutions - actual_sols extra_solutions = actual_sols - expected_solutions self.assertTrue( len(missing_solutions) == 0, 'Missing solutions:' + ','.join(str(s) for s in missing_solutions)) self.assertTrue( len(extra_solutions) == 0, 'Extra solutions:' + ','.join(str(s) for s in extra_solutions)) self.assertEqual(len(g.solutions), 4, g.solutions)
def testRuleShapeRendering(self): ''' Put 3 Tshapes in a 5x5 Grid to demo rules_shape rendering.''' g = RectangleGridPuzzle(5, 5, 'testRuleShapeRendering') TshapeDown = MultiBlock([(0, 1), (1, 1), (2, 1), (1, 0)], 'TshapeDown', can_rotate=False) TshapeRight = MultiBlock([(0, 0), (0, 1), (0, 2), (1, 1)], 'TshapeRight') TshapeLeft = MultiBlock([(0, 1), (1, 0), (1, 1), (1, 2)], 'TshapeLeft') #Ishape3Vert = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3Vert') g.inner_grid[1, 3].set_rule_shape(TshapeDown) g.inner_grid[0, 0].set_rule_shape(TshapeRight) g.inner_grid[3, 1].set_rule_shape(TshapeLeft) g.lower_left().is_entrance = True g.upper_right().is_exit = True g.render() g.generate_paths() g.load_paths() g.solve() expected_solutions = [[(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 1), (4, 1), (4, 2), (4, 3), (4, 4)]] expected_solutions = set(frozenset(p) for p in expected_solutions) print('expected_solutions:', expected_solutions) actual_solutions = set(frozenset(p) for p in g.solutions) print('actual_solutions', actual_solutions) missing_solutions = expected_solutions - actual_solutions extra_solutions = actual_solutions - expected_solutions self.assertTrue( len(missing_solutions) == 0, 'Missing solutions:' + ','.join(str(s) for s in missing_solutions)) self.assertTrue( len(extra_solutions) == 0, 'Extra solutions:' + ','.join(str(s) for s in extra_solutions))
def testMultipleShapesInPartition(self): g = RectangleGridPuzzle(5, 5, 'MultipleShapesInPartition') ''' X XXX ''' TshapeUp = MultiBlock([(0, 0), (1, 0), (2, 0), (1, 1)], 'TshapeUp') ''' XXX X ''' TshapeDown = MultiBlock([(0, 1), (1, 1), (2, 1), (1, 0)], 'TshapeDown') ''' X XX X''' TshapeLeft = MultiBlock([(0, 1), (1, 0), (1, 1), (1, 2)], 'TshapeLeft') ''' X XX X ''' TshapeRight = MultiBlock([(0, 0), (0, 1), (0, 2), (1, 1)], 'TshapeRight') Single0 = MultiBlock([(0, 1)], 'Single0') Single1 = MultiBlock([(0, 2)], 'Single1') Ishape3Vert = MultiBlock([(0, 0), (0, 1), (0, 2)], 'Ishape3Vert') # Alternate arrangement # [(0,0), (0,1), (1,1), (1,2), (2,2), (2,3), (1,3), (1,4), (2,4), (3,4), (4,4)]: # MultipleShapes soution # g.inner_grid[0, 0].set_rule_shape(TshapeUp) # g.inner_grid[1, 3].set_rule_shape(TshapeDown) # g.inner_grid[3, 1].set_rule_shape(TshapeLeft) g.inner_grid[0, 0].set_rule_shape(TshapeRight) g.inner_grid[1, 3].set_rule_shape(TshapeDown) g.inner_grid[3, 1].set_rule_shape(TshapeLeft) g.lower_left().is_entrance = True g.upper_right().is_exit = True g.generate_paths() g.load_paths() g.solve() self.assertEqual( len(g.solutions), 1, 'Wrong number of solutions: %s' % ('\n'.join([''.join(str(s)) for s in g.solutions]))) actual_solution = g.solutions[0] expected_solution = [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (3, 2), (3, 1), (4, 1), (4, 2), (4, 3), (4, 4)] self.assertEqual(list(actual_solution), expected_solution, 'Unexpected solution:\n%s' % (str(actual_solution)))
def has_shape_violation(self): if self.shape_violation is not None: raise Exception('Violation already checked') return self.shape_violation self.solution_shapes = [] rule_shapes = self.get_rule_shapes() #print('rule_shapes', rule_shapes) if self.total_rule_shape_points != len(self): self.shape_violation = True return True partition_multiblock = MultiBlock(self.keys(), name='partition_multiblock', auto_shift_Q1=False) self.shape_violation = not self.can_be_composed_of( rule_shapes, partition_multiblock, 0) return self.shape_violation
def can_be_composed_of(self, rule_shapes, partition_multiblock, depth_counter): cur_multiblock = rule_shapes[depth_counter] t = '\t' * depth_counter print(t, 'partition_multiblock', partition_multiblock) print(t, 'cur_multiblock', cur_multiblock) found_solution = False for cur_shape in cur_multiblock.rotations: print(' cur_shape', cur_shape) # Is the shape bigger than the partition? if not partition_multiblock.could_contain(cur_shape): print(' %s can' 't contain %s', partition_multiblock, cur_shape) continue # Put the shape in the lower-left corner cur_shape.set_offset(partition_multiblock.offset_point()) #print(' cur_shape.offset_point', cur_shape.offset_point()) max_shift_point = partition_multiblock.upper_right( ) - cur_shape.upper_right() #print(' max_shift_point', max_shift_point) # TODO: Change to MultiBlock yield? for y in range(max_shift_point.y + 1): for x in range(max_shift_point.x + 1): cur_shape.set_offset(Point((x, y))) abs_points = cur_shape.get_absolute_point_set() #print(' abs_points', abs_points) outside_points = abs_points - partition_multiblock if outside_points: #print(' !!outside_points', outside_points) continue # Return all the points in the partition that are left remaining_partition_points = partition_multiblock - abs_points #print(' remaining_partition_points', remaining_partition_points) # Haven't filled all our points yet... if remaining_partition_points: if depth_counter == len(rule_shapes) - 1: #print( ' still points') raise Exception('still points') else: #print(' keep on truckin...') new_partition = MultiBlock( remaining_partition_points, auto_shift_Q1=False) #print(' new_partition', new_partition) found_solution = self.can_be_composed_of( rule_shapes, new_partition, depth_counter + 1) # Filled all our points, are we at the end? else: if depth_counter == len(rule_shapes) - 1: found_solution = True else: # Should not happen till we start implementing SubtractionSquare raise Exception('Filled too soon') return False if found_solution: print('\t' * (depth_counter) + 'FOUND SOLUTION!!', cur_shape) sol_pts = cur_shape.get_absolute_point_set() self.solution_shapes.append(sol_pts) return True return False