/// <summary> /// Creates a square puzzle that starts at 1, and finishes at size * size. /// So if the size = 3 then it will produce 3 * 3 = 9: /// 1 2 3 /// 4 5 6 /// 7 8 9 /// The highest number is the "space" square. /// This is good for generating a linear "goal state" square puzzle. /// </summary> /// <param name="size">Size of the square puzzle. eg size * size. The size must be >= 2.</param> /// <returns>Returns a linear square puzzle.</returns> public static SquarePuzzle CreateLinear(int size) { SquarePuzzle goal = new SquarePuzzle(); goal.Grid = new SquareNode[size][]; for (int i = 0; i < size; i++) { goal.Grid[i] = new SquareNode[size]; } int number = 1; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { goal.Grid[i][j] = new SquareNode(new Point(i, j), number++); } } //for (int i = 0; i < size; i++) // for (int j = 0; j < size; j++) // goal.Grid[i][j].Initialize(new Node.Point(i, j), new Node.Point(i, j), number++); goal.Grid[goal.Grid.Length - 1][goal.Grid.Length - 1].IsSpace = true; return(goal); }
/// <summary> /// Sets the estimated cost, or h, for this square puzzle state. /// This heuristic uses a modified version of Nilsson's sequence score. /// If the "space" node is not in the correct position, then add 1. /// Check each node and if its neighbors are not correct, then score two per non-corect neighbor. /// Multiply this by 3. /// Add the manhatten distance for each node, except the "space" node to the total. /// </summary> /// <param name="goal">Access to the square puzzle state that is the goal for calculating the heuristic.</param> public void SetEstimatedCost(INode goal) { SquarePuzzle g = (SquarePuzzle)goal; int[] pos = { 1, -1, 0, 0, //x 0, 0, 1, -1 //y }; int[] plus = { 1, -1, 3, -3 }; // Uses Nilsson's sequence score. for (int i = 0; i < Grid.Length; i++) { for (int j = 0; j < Grid[i].Length; j++) { SquareNode node = Grid[i][j]; //Any nodes next to it that are in the "correct" spot get a +2 List <SquareNode> neighbors = Neighbors(node); for (int k = 0; k < neighbors.Count; k++) { for (int z = 0; z < 4; z++) { //todo should only check 4x1 instead of 4x4. if (neighbors[k].Position.X == node.Position.X + pos[z] && neighbors[k].Position.Y == node.Position.Y + pos[z + 4]) { if (neighbors[k].Number != node.Number + plus[z]) { EstimatedCost += 2; } } } } } } //If the goal "space" node is not "space" then add 1. if (!Grid[Grid.Length - 1][Grid.Length - 1].IsSpace) { EstimatedCost += 1; } EstimatedCost *= 3; for (int i = 0; i < Grid.Length; i++) { for (int j = 0; j < Grid[i].Length; j++) { SquareNode node = Grid[i][j]; SquareNode goalNode = GetNodeByNumber(g, node.Number); //If the node isn't where it should be then add its manhattan distance to its goal position. if (!node.IsSpace && (goalNode.Position.X != node.Position.X || goalNode.Position.Y != node.Position.Y)) { EstimatedCost += Math.Abs(node.Position.X - goalNode.Position.X) + Math.Abs(node.Position.Y - goalNode.Position.Y); } } } }
public static void Shuffle(SquarePuzzle puzzle, int iterations) { for (int i = 0; i < iterations; i++) { var children = (List <INode>)puzzle.Children; children.Shuffle(); var child = (SquarePuzzle)children[0]; var space = puzzle.GetSpace(); Swap(puzzle.Grid, space.Position, child.GetSpace().Position); } }
/// <summary> /// Gets a node in the square puzzle by its number. /// Returns null if no node has that number. /// </summary> /// <param name="p">Puzzle to search in.</param> /// <param name="num">The node with this number to get.</param> /// <returns>Returns the node in the square puzzle with the specified number.</returns> public static SquareNode GetNodeByNumber(SquarePuzzle p, int num) { for (int i = 0; i < p.Grid.Length; i++) { for (int j = 0; j < p.Grid.Length; j++) { if (p.Grid[i][j].Number == num) { return(p.Grid[i][j]); } } } return(null); }
public Program() { Current = SquarePuzzle.CreateLinear(3); SquarePuzzle.Shuffle(Current, 10); // even a small shuffle can result in huge search times Goal = SquarePuzzle.CreateLinear(3); Console.WriteLine("Starting position:"); Current.Print(); Console.WriteLine("Goal position:"); Goal.Print(); aStar = new AStar(Current, Goal); }
/// <summary> /// Returns whether or not this square puzzle state is equal to the parameter square puzzle state. /// </summary> /// <param name="state">State to compare for equality.</param> /// <returns>Returns whether or not this square puzzle state is equal to the parameter square puzzle state.</returns> public bool Equal(INode state) { SquarePuzzle s = (SquarePuzzle)state; for (int i = 0; i < s.Grid.Length; i++) { for (int j = 0; j < s.Grid[i].Length; j++) { if (s.Grid[i][j].Number != Grid[i][j].Number) { return(false); } } } return(true); }
/// <summary> /// Generates a random square puzzle of size * size. /// Not all random puzzles are solvable. /// </summary> /// <param name="size">Size of the square puzzle. eg size * size. The size must be >= 2.</param> public static SquarePuzzle CreateRandom(int size) { SquarePuzzle start = new SquarePuzzle(); Random rand = new Random(); start.Grid = new SquareNode[size][]; for (int i = 0; i < size; i++) { start.Grid[i] = new SquareNode[size]; } int number = 1; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { int x; int y; while (true) { x = rand.Next(0, size); y = rand.Next(0, size); if (start.Grid[x][y] != null) { continue; } break; } start.Grid[x][y] = new SquareNode(new Point(x, y), number++); if (i == size - 1 && j == size - 1) { start.Grid[x][y].IsSpace = true; } } } return(start); }
/// <summary> /// Creates a copy of the square puzzle that is passed. /// </summary> public static SquarePuzzle Copy(SquarePuzzle puzzle) { SquarePuzzle copy = new SquarePuzzle(); copy.Grid = new SquareNode[puzzle.Grid.Length][]; for (int i = 0; i < puzzle.Grid.Length; i++) { copy.Grid[i] = new SquareNode[puzzle.Grid.Length]; } for (int i = 0; i < puzzle.Grid.Length; i++) { for (int j = 0; j < puzzle.Grid.Length; j++) { copy.Grid[i][j] = new SquareNode(puzzle.Grid[i][j].Position, puzzle.Grid[i][j].Number); if (puzzle.Grid[i][j].IsSpace) { copy.Grid[i][j].IsSpace = true; } } } return(copy); }
/// <summary> /// /// </summary> /// <param name="userDefined"></param> /// <returns></returns> public static SquarePuzzle CreateUserDefined(int[][] userDefined) { //Error checking, make sure that the provided arrays are qualified to make a square puzzle. for (int i = 0; i < userDefined.Length; i++) { if (userDefined.Length != userDefined[i].Length) { return(null); } } int[][] errorUserDefined = new int[userDefined.Length][]; for (int i = 0; i < errorUserDefined.Length; i++) { errorUserDefined[i] = new int[errorUserDefined.Length]; } for (int i = 0; i < errorUserDefined.Length; i++) { for (int j = 0; j < errorUserDefined.Length; j++) { errorUserDefined[i][j] = new int(); } } int currentNumber = 1; for (int i = 0; i < userDefined.Length; i++) { for (int j = 0; j < userDefined.Length; j++) { for (int k = 0; k < userDefined.Length; k++) { for (int z = 0; z < userDefined.Length; z++) { if (userDefined[k][z] == currentNumber) { if (errorUserDefined[i][j] == currentNumber) //check for duplicates. { return(null); } errorUserDefined[i][j] = currentNumber; } } } currentNumber++; } } currentNumber = 1; for (int i = 0; i < userDefined.Length; i++) { for (int j = 0; j < userDefined.Length; j++) { if (errorUserDefined[i][j] != currentNumber) // check to make sure we have 1 through length * length { return(null); } currentNumber++; } } //End error checking. SquarePuzzle puzzle = new SquarePuzzle(); puzzle.Grid = new SquareNode[userDefined.Length][]; for (int i = 0; i < userDefined.Length; i++) { puzzle.Grid[i] = new SquareNode[userDefined.Length]; } for (int i = 0; i < userDefined.Length; i++) { for (int j = 0; j < userDefined.Length; j++) { puzzle.Grid[i][j] = new SquareNode(new Point(i, j), userDefined[i][j]); // if it is the highest number, then it is the "space". if (userDefined[i][j] == userDefined.Length * userDefined.Length) { puzzle.Grid[i][j].IsSpace = true; } } } return(puzzle); }
/// <summary> /// No space exception is thrown when a SquarePuzzle does not contain a ' ' node. /// </summary> /// <param name="puzzle">The invalid square puzzle.</param> public NoSpaceException(SquarePuzzle puzzle) : base("No space square was found in this square puzzle state.") { Puzzle = puzzle; }