def test_scrambled_cube(self): '''Scrambling the cube with cannot return a solved cube. This is guaranteed for 2 and 3 rotations, because scramble_cube does not apply two consecutive rotations that cancel each other. ''' self.assertNotEqual(cube_lib.get_scrambled_cube(num_rotations=2), cube_lib.Cube()) self.assertNotEqual(cube_lib.get_scrambled_cube(num_rotations=3), cube_lib.Cube())
def evaluate_solver(solver_fn: Callable[[cube_lib.Cube], Solver], trajectory_length: int, max_num_steps: int, num_trials: int): '''Evaluates a solver. Uses the solver to solves `num_trials` cubes that are scrambled with `trajectory_length` random rotations. Returns a dataframe with statistics about the performance of the solver. ''' evaluation_results = pd.DataFrame() for _ in range(num_trials): cube = cube_lib.get_scrambled_cube(trajectory_length) solver = solver_fn(cube) num_steps = num_steps_to_solve(solver, max_num_steps) evaluation_results = evaluation_results.append( { 'num_steps_scrambled': trajectory_length, 'num_steps_to_solve': num_steps, 'solved': solver.cube.is_solved() }, ignore_index=True) evaluation_results.num_steps_scrambled = ( evaluation_results.num_steps_scrambled.astype('int')) # We can't convert 'num_steps_to_solve' to ints, because it contains NAs, # which are of type float. evaluation_results.num_steps_to_solve = ( evaluation_results.num_steps_to_solve.astype('float')) evaluation_results.solved = evaluation_results.solved.astype('bool') return evaluation_results
def test_priority_queue_same_value(self): '''Verifies that we can insert two states with the same value.''' queue = solver_lib._PriorityQueue() first_cube = cube_lib.get_scrambled_cube(num_rotations=3) first_state = solver_lib._AStarState(first_cube, cost_to_come=2, est_cost_to_go=5, previous_state=None, previous_rotation=None) queue.add_or_update_state(first_state) second_cube = first_cube.copy() second_cube.rotate_face( cube_lib.Rotation(cube_lib.Face.LEFT, is_clockwise=True)) second_state = solver_lib._AStarState(second_cube, cost_to_come=2, est_cost_to_go=5, previous_state=None, previous_rotation=None) queue.add_or_update_state(second_state) self.assertTrue(queue) costs_to_come = set() costs_to_come.add(queue.pop_min_state().cube) self.assertTrue(queue) costs_to_come.add(queue.pop_min_state().cube) self.assertFalse(queue) self.assertEqual(costs_to_come, set((first_cube, second_cube)))
def greedy_solver(depth=3): '''Runs the greedy solver. Results (2020/05/20): 3.4 cubes solved / second at depth 3. ''' model = trainer.create_model() num_starting_cubes = 20 num_runs = 3 num_runs_done = 0 time_elapsed = 0 for _ in range(num_starting_cubes): cube = cube_lib.get_scrambled_cube(depth) begin_time = time.monotonic() for _ in range(num_runs): solver = solver_lib.GreedySolver(cube, model, depth) num_rotations = 0 while not solver.cube.is_solved(): if num_rotations == depth: raise Exception( 'Done {} rotations, cube still not solved'.format( depth)) solver.apply_next_rotation() num_rotations += 1 end_time = time.monotonic() time_elapsed += (end_time - begin_time) num_runs_done += num_runs print('{} cubes solved per second at depth {}'.format( num_runs_done / time_elapsed, depth))
def test_priority_queue(self): queue = solver_lib._PriorityQueue() first_cube = cube_lib.get_scrambled_cube(3) first_state = solver_lib._AStarState(first_cube, cost_to_come=2, est_cost_to_go=5, previous_state=None, previous_rotation=None) queue.add_or_update_state(first_state) second_cube = first_cube.copy() second_cube.rotate_face( cube_lib.Rotation(cube_lib.Face.LEFT, is_clockwise=True)) second_state = solver_lib._AStarState(second_cube, cost_to_come=1, est_cost_to_go=7, previous_state=None, previous_rotation=None) queue.add_or_update_state(second_state) third_cube = second_cube.copy() third_cube.rotate_face( cube_lib.Rotation(cube_lib.Face.LEFT, is_clockwise=True)) third_state = solver_lib._AStarState(third_cube, cost_to_come=1, est_cost_to_go=5, previous_state=None, previous_rotation=None) queue.add_or_update_state(third_state) self.assertTrue(queue) self.assertEqual(queue.pop_min_state(), third_state) second_state = copy.copy(second_state) second_state.est_cost_to_go = 3 queue.add_or_update_state(second_state) self.assertTrue(queue) self.assertEqual(queue.pop_min_state(), second_state) first_state = copy.copy(first_state) first_state.est_cost_to_go = 3 queue.add_or_update_state(first_state) self.assertTrue(queue) self.assertEqual(queue.pop_min_state(), first_state) self.assertFalse(queue)
def fraction_solved_greedy(model: tf.keras.Model, trajectory_length: int, num_trials: int, greedy_depth: int) -> float: '''Computes the fraction of random cubes that can be solved greedily.''' num_solved = 0 for _ in range(num_trials): cube = cube_lib.get_scrambled_cube(trajectory_length) solver = solver_lib.GreedySolver(cube, model, depth=greedy_depth) for _ in range(trajectory_length): if solver.cube.is_solved(): break solver.apply_next_rotation() if solver.cube.is_solved(): num_solved += 1 return num_solved / num_trials