// Copy constructor. Sharing amphipod references doesn't matter. public c_burrow(c_burrow other) : this(other.home_depth) { for (int column = 0; column < burrow_positions.Length; column++) { for (int row = 0; row < burrow_positions[column].Length; row++) { burrow_positions[column][row] = other.burrow_positions[column][row]; } } }
// Try to solve the burrow public (bool, int) try_solve(bool pretty, List <c_burrow> solution_stack, string depth_string = "") { solution_stack.Add(new c_burrow(this)); if (pretty) { display("try_solve", depth_string); } int cost = 0; // First try to move any amphipods to their solved positions. This is always the best thing to do. int clear_cost = int.MaxValue; while (clear_cost > 0) { clear_cost = 0; clear_cost += try_clear_hallway(); clear_cost += try_clear_homes(); cost += clear_cost; } // if some amphipods were moved to their homes, note that in the solution stack. if (cost > 0) { solution_stack.Add(new c_burrow(this)); if (pretty) { display(string.Format("moved some home (cost = {0})", cost), depth_string); } } // If the burrow is solved, return. if (is_solved()) { if (pretty) { Console.WriteLine(depth_string + "solved!"); } return(true, cost); } // I don't know what to do now but we have to move somebody out of a burrow. // So try using brute force on every possible move and use the best result we find. // ... turns out this still solves things in about a second. Yay! List <c_burrow> best_guess_stack = null; bool best_guess_solved = false; int best_guess_cost = int.MaxValue; // Loop through each home we could pull an amphipod out of foreach (int start_column in k_home_columns) { (c_amphipod start, int start_row) = find_top_in_unfinished_home(start_column); // If the top amphipod in this home was found if (start != null) { (int min_end_column, int max_end_column) = get_valid_parking_spots(start_column); // Loop through each place we could move it to foreach (int end_column in k_hallway_parking_spot_columns) { if (end_column >= min_end_column && end_column <= max_end_column) { c_amphipod end = this.burrow_positions[end_column][0]; // If we find an empty spot to move it to if (end == null) { // Move the amphipod to that spot in the hallway and recurse. c_burrow guess = new c_burrow(this); guess.burrow_positions[end_column][0] = start; guess.burrow_positions[start_column][start_row] = null; List <c_burrow> guess_stack = new List <c_burrow>(); (bool guess_solved, int guess_cost) = guess.try_solve(pretty, guess_stack, depth_string + "\t"); // If this branch of recursion solved the burrow and it's the best cost we've found so far, save the results. if (guess_solved) { int initial_guess_cost = (start_row + Math.Abs(end_column - start_column)) * start.cost_per_move; guess_cost += initial_guess_cost; if (guess_cost < best_guess_cost) { best_guess_solved = true; best_guess_cost = guess_cost; best_guess_stack = guess_stack; } } } } } } } // Some branch of recursion found a solution. if (best_guess_solved) { if (pretty) { Console.WriteLine(depth_string + "a guess solved"); } solution_stack.AddRange(best_guess_stack); return(best_guess_solved, cost + best_guess_cost); } // No solution was found. else { if (pretty) { Console.WriteLine(depth_string + "no guesses solved"); } return(false, 0); } }