How to resolve the algorithm 15 puzzle solver step by step in the C# programming language
How to resolve the algorithm 15 puzzle solver step by step in the C# programming language
Table of Contents
Problem Statement
Your task is to write a program that finds a solution in the fewest moves possible single moves to a random Fifteen Puzzle Game. For this task you will be using the following puzzle:
The output must show the moves' directions, like so: left, left, left, down, right... and so on. There are two solutions, of fifty-two moves: rrrulddluuuldrurdddrullulurrrddldluurddlulurruldrdrd rrruldluuldrurdddluulurrrdlddruldluurddlulurruldrrdd see: Pretty Print of Optimal Solution Finding either one, or both is an acceptable result. Solve the following problem:
Let's start with the solution:
Step by Step solution about How to resolve the algorithm 15 puzzle solver step by step in the C# programming language
This C# code implements a solver for the 15 puzzle, a classic sliding tile puzzle. Here's a detailed breakdown:
1. Namespaces:
using System;
: Provides core functionalities likeConsole
for output.using System.Collections.Generic;
: Provides generic collection types likeList<>
andHashSet<>
.using System.Linq;
: Provides extension methods likeSequenceEqual
for efficient collection comparison.
2. Class Definition (Puzzle15Solver
):
Main
method:- Defines the starting configuration of the puzzle tiles as a
List<int>
. - Sets the initial position of the empty tile (
0
) withzeroIndex
. - Creates an initial
Puzzle
object with the starting state. - Initializes two data structures:
openSet
: APriorityQueue<Puzzle>
to store puzzle states prioritized by a heuristic function.closedSet
: AHashSet<Puzzle>
to store explored puzzle states for avoiding revisiting.
- Enqueues the initial puzzle in
openSet
. - Prints the starting state and enters a loop until the solution is found.
- Inside the loop, calls the
Search
function to explore possible moves. - After finding the solution, prints the sequence of moves, number of steps, and number of explored states.
- Defines the starting configuration of the puzzle tiles as a
3. Search Function (Search
):
- Dequeues the highest priority puzzle state (closest to the goal) from
openSet
. - Adds the explored state to
closedSet
. - Extracts the zero tile's position (row and column) from the current puzzle.
- Iterates through all four possible directions (Left, Right, Up, Down).
- For each direction:
- Creates a deep copy of the current puzzle using the
Clone
method. - Simulates the move by updating the tile positions and move history.
- Calls
EvaluateNextPuzzle
to analyze the generated state.
- Creates a deep copy of the current puzzle using the
- For each direction:
4. Evaluate Next Puzzle Function (EvaluateNextPuzzle
):
- Checks if the newly generated puzzle state is already explored (
closedSet
). - If not explored:
- Enqueues the new state in
openSet
based on its heuristic value. - If the new state matches the goal configuration (
GOAL
), sets it as the solution (solution
).
- Enqueues the new state in
5. Move Enum (Move
):
- Defines an enumeration for the four possible move directions with their corresponding symbols and step values for updating tile positions.
6. Puzzle Class (Puzzle
):
- Represents a single puzzle state.
- Constructor: Initializes the puzzle state with tiles, move history, empty tile position, and search depth.
MakeMove
method:- Simulates a move by swapping the empty tile with a neighboring tile based on the provided
Move
direction. - Updates the empty tile position and move history.
- Checks if the new state is already explored (
closedSet
).- If not explored:
- Checks if it's the goal state (
GOAL
). If so, sets it as the solution (solution
). - Otherwise, enqueues it in
openSet
for further exploration.
- Checks if it's the goal state (
- If not explored:
- Simulates a move by swapping the empty tile with a neighboring tile based on the provided
Heuristic
method:- Calculates a heuristic value representing the estimated distance from the current state to the goal state.
- It uses the Manhattan distance, which is the sum of the absolute differences between the current and goal positions of each tile.
- It also adds the current search depth to prioritize solutions requiring fewer moves.
Clone
method: Creates a deep copy of the current puzzle state to avoid modifying the original state during exploration.Display
method: Prints the current puzzle state in a grid format.Equals
andGetHashCode
methods: Override default implementations for efficient comparison and hash code generation based on the tile configuration, allowing for faster set operations inclosedSet
andopenSet
.
Overall, this code implements a breadth-first search algorithm with a heuristic function to efficiently solve the 15 puzzle.
Source code in the csharp programming language
using System;
using System.Collections.Generic;
using System.Linq;
public class Puzzle15Solver
{
public static void Main(string[] args)
{
List<int> start = new List<int> { 15, 14, 1, 6, 9, 11, 4, 12, 0, 10, 7, 3, 13, 8, 5, 2 };
int zeroIndex = 8;
Puzzle initial = new Puzzle(start, new List<string>(), zeroIndex, 0);
Queue<Puzzle> openSet = new PriorityQueue<Puzzle>((p1, p2) => p1.Heuristic().CompareTo(p2.Heuristic()));
HashSet<Puzzle> closedSet = new HashSet<Puzzle>();
openSet.Enqueue(initial);
Console.WriteLine("Solving the 15 puzzle:");
initial.Display();
while (solution == null)
{
Search(openSet, closedSet);
}
Console.WriteLine(string.Join("", solution.Moves));
Console.WriteLine("Number of steps: " + solution.Moves.Count);
Console.WriteLine("Number of puzzle states checked: " + closedSet.Count);
}
private static void Search(Queue<Puzzle> openSet, HashSet<Puzzle> closedSet)
{
Puzzle current = openSet.Dequeue();
closedSet.Add(current);
int zeroIndex = current.ZeroIndex;
int row = zeroIndex / 4;
int column = zeroIndex % 4;
if (column > 0)
{
Puzzle nextPuzzle = current.Clone();
nextPuzzle.MakeMove(Move.Left);
EvaluateNextPuzzle(openSet, closedSet, nextPuzzle);
}
if (column < 3)
{
Puzzle nextPuzzle = current.Clone();
nextPuzzle.MakeMove(Move.Right);
EvaluateNextPuzzle(openSet, closedSet, nextPuzzle);
}
if (row > 0)
{
Puzzle nextPuzzle = current.Clone();
nextPuzzle.MakeMove(Move.Up);
EvaluateNextPuzzle(openSet, closedSet, nextPuzzle);
}
if (row < 3)
{
Puzzle nextPuzzle = current.Clone();
nextPuzzle.MakeMove(Move.Down);
EvaluateNextPuzzle(openSet, closedSet, nextPuzzle);
}
}
private static void EvaluateNextPuzzle(Queue<Puzzle> openSet, HashSet<Puzzle> closedSet, Puzzle nextPuzzle)
{
if (!closedSet.Contains(nextPuzzle))
{
openSet.Enqueue(nextPuzzle);
if (nextPuzzle.Tiles.SequenceEqual(Puzzle.GOAL))
{
solution = nextPuzzle;
}
}
}
public enum Move
{
Left("L", -1),
Right("R", +1),
Up("U", -4),
Down("D", +4);
private Move(string symbol, int step)
{
Symbol = symbol;
Step = step;
}
public string Symbol { get; private set; }
public int Step { get; private set; }
}
public class Puzzle
{
public Puzzle(List<int> tiles, List<string> moves, int zeroIndex, int searchDepth)
{
Tiles = tiles;
Moves = moves;
ZeroIndex = zeroIndex;
SearchDepth = searchDepth;
}
public void MakeMove(Move move)
{
int temp = Tiles[ZeroIndex + move.Step];
Tiles[ZeroIndex + move.Step] = 0;
Tiles[ZeroIndex] = temp;
ZeroIndex += move.Step;
Moves.Add(move.Symbol);
if (!closedSet.Contains(this))
{
if (Tiles.SequenceEqual(Puzzle.GOAL))
{
solution = this;
}
else
{
openSet.Enqueue(this);
}
}
}
public int Heuristic()
{
int distance = 0;
for (int i = 0; i < Tiles.Count; i++)
{
int tile = Tiles[i];
if (tile > 0)
{
distance += Math.Abs((i / 4) - ((tile - 1) / 4)) + Math.Abs((i % 4) - ((tile - 1) % 4));
}
}
return distance + SearchDepth;
}
public Puzzle Clone()
{
return new Puzzle(new List<int>(Tiles), new List<string>(Moves), ZeroIndex, SearchDepth + 1);
}
public void Display()
{
for (int i = 0; i < Tiles.Count; i++)
{
Console.Write(String.Format("{0}{1,2}{2}", (i % 4 == 0) ? "[" : "", Tiles[i], (i % 4 == 3) ? "]\n" : " "));
}
Console.WriteLine();
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
Puzzle other = (Puzzle)obj;
return Tiles.SequenceEqual(other.Tiles);
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + Tiles.GetHashCode();
hash = hash * 23 + ZeroIndex;
return hash;
}
}
public List<int> Tiles { get; private set; }
public List<string> Moves { get; private set; }
public int ZeroIndex { get; private set; }
public int SearchDepth { get; private set; }
private static readonly List<int> GOAL = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 };
}
private static Puzzle solution;
}
You may also check:How to resolve the algorithm Literals/Integer step by step in the PicoLisp programming language
You may also check:How to resolve the algorithm Loops/Wrong ranges step by step in the Ruby programming language
You may also check:How to resolve the algorithm Superpermutation minimisation step by step in the Go programming language
You may also check:How to resolve the algorithm Carmichael 3 strong pseudoprimes step by step in the Racket programming language
You may also check:How to resolve the algorithm Faulhaber's formula step by step in the Wren programming language