def test_invalid_grid(self): """ The only valid grids are all members of GRID.keys() (1,2,) (1,3,) (2,1,) (2,2,) (2,3,) (2,4,) (3,2,) (3,3,) """ state = dict(grid8.GRID) # create a new grid grid_square = (1, 9,) with self.assertRaises(KeyError): grid8.assign_to_grid(grid_square, 1, state)
def test_assign_to_filled_space(self): """ Once a grid square has a value it cannot acccept any more values. raise ValueError """ state = dict(grid8.GRID) # create a new grid grid_square = (1, 2,) new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1) with self.assertRaises(ValueError): grid8.assign_to_grid(grid_square, 1, new_state)
def test_invalid_grid(self): """ The only valid grids are all members of GRID.keys() (1,2,) (1,3,) (2,1,) (2,2,) (2,3,) (2,4,) (3,2,) (3,3,) """ state = dict(grid8.GRID) # create a new grid grid_square = ( 1, 9, ) with self.assertRaises(KeyError): grid8.assign_to_grid(grid_square, 1, state)
def test_assign_to_filled_space(self): """ Once a grid square has a value it cannot acccept any more values. raise ValueError """ state = dict(grid8.GRID) # create a new grid grid_square = ( 1, 2, ) new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1) with self.assertRaises(ValueError): grid8.assign_to_grid(grid_square, 1, new_state)
def test_assignment_is_valid(self): """ RULES: - No consecutive numbers may appear next to each other either vertically, horizontally or diagonally. - Each number may only be used once """ state = dict(grid8.GRID) # create a new grid grid_square = (1, 2,) is_valid = grid8.assignment_valid(grid_square, 1, state) # Assert the proposed move is valid self.assertTrue(is_valid) # Assign the move new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1) ### All slots next to grid(1, 2,) should be invalid grid_square = (1, 3,) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = (2, 1,) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = (2, 2,) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = (2, 3,) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid)
def test_assign_to_grid(self): """ Can we assign a number to a valid square on the grid """ state = dict(grid8.GRID) # create a new grid grid_square = (1, 2,) new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert the state changed after the assignemnt self.assertNotEqual(new_state, state) # Assert 1 is on the board self.assertIn(1, new_state.values()) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1)
def test_assignment_is_valid(self): """ RULES: - No consecutive numbers may appear next to each other either vertically, horizontally or diagonally. - Each number may only be used once """ state = dict(grid8.GRID) # create a new grid grid_square = ( 1, 2, ) is_valid = grid8.assignment_valid(grid_square, 1, state) # Assert the proposed move is valid self.assertTrue(is_valid) # Assign the move new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1) ### All slots next to grid(1, 2,) should be invalid grid_square = ( 1, 3, ) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = ( 2, 1, ) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = ( 2, 2, ) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid) grid_square = ( 2, 3, ) is_valid = grid8.assignment_valid(grid_square, 2, new_state) # Assert the proposed move is valid self.assertFalse(is_valid)
def test_assign_to_grid(self): """ Can we assign a number to a valid square on the grid """ state = dict(grid8.GRID) # create a new grid grid_square = ( 1, 2, ) new_state = grid8.assign_to_grid(grid_square, 1, state) # Assert the state changed after the assignemnt self.assertNotEqual(new_state, state) # Assert 1 is on the board self.assertIn(1, new_state.values()) # Assert 1 is in the right position. self.assertEqual(new_state[grid_square], 1)
def solve(state): """ Recursive solution that uses a brute force approach to findindg the solution. 2 essential facts are derived from the board state. - The number of empty spaces left. - The choice of numbers remaining. Solve for a given state The base case: If no there is no empty spaces the game is over so retrun the end state. The recursive case: While there are empty spaces, AND while there are choices remaining. If you can make a valid assignment, then assign the number to the board to create a new state. Solve for the new state. <------ Recursion, back to the top with a new step Else Try another number in this space. OR None of the numbers fit here so try them in another space. Everything failed :param dict state: the state of the board :return dict: the end state of the game. None means no solution. """ # uncomment the line below if you would like to see a trace. # print state # Derive the number of empty spaces left and the choices remaining to go on the board empty_spaces = [g for g, v in state.items() if v is None] numbers_used = [v for g, v in state.items() if v] numbers_left = [ n for n in ( 1, 2, 3, 4, 5, 6, 7, 8, ) if n not in numbers_used ] ### BASE CASE if not empty_spaces: return state ### RECURSIVE CASE while empty_spaces: # Treat the remaining options like a stack grid_square = empty_spaces.pop(0) # Grab the next space available while numbers_left: value = numbers_left.pop(0) # Grab the next number available # If we can make an assignment if grid8.assignment_valid(grid_square, value, state): # Make a new state by assiging a number new_state = grid8.assign_to_grid(grid_square, value, state) ### RECURSION # Try to solve things from this state # Remember, if the base above is returned then this is where we will catch it. new_state = solve(new_state) if new_state: # return the solution return new_state # If the state was none we will implicitly try the remaining numbers or spaces # in this branch of the solution tree. # No sweat, if this branch is exhausted return None. # This may be the last branch meaning the state we started in coudln't be solved. return None
+---+---+ | {0} | {1} | +---+---+---+---+ | {2} | {3} | {4} | {5} | +---+---+---+---+ | {6} | {7} | +---+---+\n""" # Cycle through all the possible starting points to see how many solutions there are. for start in ( 1, 2, 3, 4, 5, 6, 7, 8, ): # Assign a starting point. new_state = grid8.assign_to_grid(( 1, 2, ), start, grid8.GRID) # try to solve from here solution = solve(new_state) if solution: # print a solution if we got one values = [solution[k] for k in sorted(solution.keys())] print board.format(*values)
def solve(state): """ Recursive solution that uses a brute force approach to findindg the solution. 2 essential facts are derived from the board state. - The number of empty spaces left. - The choice of numbers remaining. Solve for a given state The base case: If no there is no empty spaces the game is over so retrun the end state. The recursive case: While there are empty spaces, AND while there are choices remaining. If you can make a valid assignment, then assign the number to the board to create a new state. Solve for the new state. <------ Recursion, back to the top with a new step Else Try another number in this space. OR None of the numbers fit here so try them in another space. Everything failed :param dict state: the state of the board :return dict: the end state of the game. None means no solution. """ # uncomment the line below if you would like to see a trace. # print state # Derive the number of empty spaces left and the choices remaining to go on the board empty_spaces = [g for g, v in state.items() if v is None] numbers_used = [v for g, v in state.items() if v] numbers_left = [n for n in (1, 2, 3, 4, 5, 6, 7, 8,) if n not in numbers_used] # BASE CASE if not empty_spaces: return state # RECURSIVE CASE while empty_spaces: # Treat the remaining options like a stack grid_square = empty_spaces.pop(0) # Grab the next space available while numbers_left: value = numbers_left.pop(0) # Grab the next number available # If we can make an assignment if grid8.assignment_valid(grid_square, value, state): # Make a new state by assiging a number new_state = grid8.assign_to_grid(grid_square, value, state) # RECURSION # Try to solve things from this state # Remember, if the base above is returned then this is where we will catch it. new_state = solve(new_state) if new_state: # return the solution return new_state # If the state was none we will implicitly try the remaining numbers or spaces # in this branch of the solution tree. # No sweat, if this branch is exhausted return None. # This may be the last branch meaning the state we started in coudln't be solved. return None
return new_state # If the state was none we will implicitly try the remaining numbers or spaces # in this branch of the solution tree. # No sweat, if this branch is exhausted return None. # This may be the last branch meaning the state we started in coudln't be solved. return None if __name__ == "__main__": # create a board for printing purposes board = """SOLUTION!! :-) +---+---+ | {0} | {1} | +---+---+---+---+ | {2} | {3} | {4} | {5} | +---+---+---+---+ | {6} | {7} | +---+---+\n""" # Cycle through all the possible starting points to see how many solutions there are. for start in (1, 2, 3, 4, 5, 6, 7, 8,): # Assign a starting point. new_state = grid8.assign_to_grid((1, 2,), start, grid8.GRID) # try to solve from here solution = solve(new_state) if solution: # print a solution if we got one values = [solution[k] for k in sorted(solution.keys())] print(board.format(*values))