/// <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); } } } }
/// <summary> /// Swaps the two nodes in the grid provided. /// Is used when generating children. /// Copy the parent and swap for the next move. /// </summary> private static void Swap(SquareNode[][] grid, Point p1, Point p2) { grid[p1.X][p1.Y].Position = p2; grid[p2.X][p2.Y].Position = p1; SquareNode temp = grid[p2.X][p2.Y]; grid[p2.X][p2.Y] = grid[p1.X][p1.Y]; grid[p1.X][p1.Y] = temp; }
/// <summary> /// Gets the space node of this square puzzle. /// </summary> /// <returns></returns> public SquareNode GetSpace() { SquareNode space = null; for (int i = 0; i < Grid.Length; i++) { for (int j = 0; j < Grid[i].Length; j++) { if (Grid[i][j].IsSpace) { space = Grid[i][j]; goto found; // found it so break out of the two for loops } } } throw new NoSpaceException(this); found: return(space); }
/// <summary> /// Returns up to all four neighbors of the node that is passed. /// </summary> /// <param name="node">Node to get its neighbors.</param> /// <returns>Returns up to all four neighbors of the node that is passed.</returns> public List <SquareNode> Neighbors(SquareNode node) { int[] n = { 1, -1, 0, 0, //x 0, 0, 1, -1 }; //y var p = node.Position; var neighbors = new List <SquareNode>(); for (int i = 0; i < 4; i++) { int x = p.X + n[i]; int y = p.Y + n[i + 4]; if (x >= 0 && x < Grid.Length && y >= 0 && y < Grid.Length) { neighbors.Add(Grid[x][y]); } } return(neighbors); }