Solving N-Queens Problem the C# Way
Problem Statement The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle.
The intuition for any recursive solution is
There are certain cases where this happens naturally. e.g. a purely recursive call like a fibonacci snippet does not need to keep track of visited nodes.
The below code starts off with initializing the 2d-board. The recursive call parameters has the board, the size of the board and the hashsets to keep track of the visited nodes.
|
|
Next step, is to write the recursion method.
|
|
LINQ (Language-Integrated Query) provides query syntax similar to SQL that allows you to modify objects. LINQ provides both method syntax and query syntax which differs syntactically, but the functionality is the same.
Enumerable.Repeat(".", 5).ToArray(); //creates a string array with 5 "."
Enumerable.Range(0, 5).Select(_ => ".").ToArray(); //Also creates a string array with 5 "."
Now, we can use the combination of Enumerable.Repeat and Enumerable.Range to create a LINQ expression. In the below code, for each element of the range, an array of “.” is created. The sequence of arrays is now converted into array of arrays, creating a 2D array.
|
|
We also have another for loop that we can convert into LINQ expression. The below code creates a range and iterates through the range to create another range. For each cell in the board gets concatenated into a string.
|
|
In C# 12, there is a new feature that allows you to declare complex data types into an alias.
using VisitedSets = (System.Collections.Generic.HashSet<int> row,
System.Collections.Generic.HashSet<int> upperDiag,
System.Collections.Generic.HashSet<int> lowerDiag);
using Values = (int row, int upperDiag, int lowerDiag);
Now rather than declaring three separated HashSets, we could declare it all as one single variable and use this to pass it in the methods.
var visitedSets = new VisitedSets(
new(),
new(),
new()
);
Values values = (row, row - col, row + col);
Now, we can move the logic to mark the nodes as visited and unvisited into its own methods. Rather than passing in so many parameters in the method, we succesfully refactored so that we can pass a collection of objects, in this case tuples.
private static void MarkUnVisited(VisitedSets visitedSets, Values values)
{
visitedSets.row.Remove(values.row);
visitedSets.upperDiag.Remove(values.lowerDiag);
visitedSets.lowerDiag.Remove(values.upperDiag);
}
private static void MarkVisited(VisitedSets visitedSets, Values values)
{
visitedSets.row.Add(values.row);
visitedSets.upperDiag.Add(values.upperDiag);
visitedSets.lowerDiag.Add(values.lowerDiag);
}
For full code before and after refactoring, please refer here:
Problem Statement The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle.