Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions Algorithms.Tests/Problems/NQueens/BacktrackingNQueensSolverTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Linq;
using Algorithms.Problems.NQueens;

using FluentAssertions;

using NUnit.Framework;

namespace Algorithms.Tests.Problems.NQueens
{
public static class BacktrackingNQueensSolverTests
{
[TestCase(0, 0)]
[TestCase(1, 1)]
[TestCase(2, 0)]
[TestCase(3, 0)]
[TestCase(4, 2)]
[TestCase(5, 10)]
[TestCase(6, 4)]
[TestCase(7, 40)]
[TestCase(8, 92)]
[TestCase(8, 92)]
[TestCase(9, 352)]
[TestCase(10, 724)]
[TestCase(11, 2680)]
[TestCase(12, 14200)]
public static void SolvesCorrectly(int n, int expectedNumberOfSolutions)
{
// Arrange
// Act
var result = new BacktrackingNQueensSolver().BacktrackSolve(n).ToList();

// Assert
result.Should().HaveCount(expectedNumberOfSolutions);
foreach (var solution in result)
{
ValidateOneQueenPerRow(solution);
ValidateOneQueenPerColumn(solution);
ValidateOneQueenPerTopLeftBottomRightDiagonalLine(solution);
ValidateOneQueenPerBottomLeftTopRightDiagonalLine(solution);
}
}

[Test]
public static void NCannotBeNegative()
{
var n = -1;

Action act = () => new BacktrackingNQueensSolver().BacktrackSolve(n);

act.Should().Throw<ArgumentException>();
}

private static void ValidateOneQueenPerRow(bool[,] solution)
{
for (var i = 0; i < solution.GetLength(1); i++)
{
var foundQueen = false;
for (var j = 0; j < solution.GetLength(0); j++)
{
foundQueen = ValidateCell(foundQueen, solution[j, i]);
}
}
}

private static void ValidateOneQueenPerColumn(bool[,] solution)
{
for (var i = 0; i < solution.GetLength(0); i++)
{
var foundQueen = false;
for (var j = 0; j < solution.GetLength(1); j++)
{
foundQueen = ValidateCell(foundQueen, solution[i, j]);
}
}
}

private static void ValidateOneQueenPerTopLeftBottomRightDiagonalLine(bool[,] solution)
{
for (var i = 0; i < solution.GetLength(0); i++)
{
var foundQueen = false;
for (var j = 0; i + j < solution.GetLength(1); j++)
{
foundQueen = ValidateCell(foundQueen, solution[i + j, i]);
}
}

for (var i = 0; i < solution.GetLength(1); i++)
{
var foundQueen = false;
for (var j = 0; i + j < solution.GetLength(0); j++)
{
foundQueen = ValidateCell(foundQueen, solution[j, i + j]);
}
}
}

private static void ValidateOneQueenPerBottomLeftTopRightDiagonalLine(bool[,] solution)
{
for (var i = 0; i < solution.GetLength(0); i++)
{
var foundQueen = false;
for (var j = 0; i - j >= 0; j++)
{
foundQueen = ValidateCell(foundQueen, solution[i - j, i]);
}
}

for (var i = 0; i < solution.GetLength(1); i++)
{
var foundQueen = false;
for (var j = 0; i - j >= 0 && solution.GetLength(0) - j > 0; j++)
{
foundQueen = ValidateCell(foundQueen, solution[solution.GetLength(0) - j - 1, i - j]);
}
}
}

static bool ValidateCell(bool foundQueen, bool currentCell)
{
if (foundQueen)
{
currentCell.Should().BeFalse();
}

return foundQueen || currentCell;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
using System.Collections.Generic;
using System.Linq;

using Algorithms.Problems.StableMarriage;

using NUnit.Framework;

namespace Algorithms.Problems.StableMarriage
namespace Algorithms.Tests.Problems.StableMarriage
{
/// <summary>
/// The stable marriage problem (also stable matching problem or SMP)
Expand Down
108 changes: 108 additions & 0 deletions Algorithms/Problems/NQueens/BacktrackingNQueensSolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;

namespace Algorithms.Problems.NQueens
{
public class BacktrackingNQueensSolver
{
/// <summary>
/// Solves N-Queen Problem given a n dimension chessboard and using backtracking with recursion algorithm.
/// If we find a dead-end within or current solution we go back and try another position for queen.
/// </summary>
/// <param name="n">Number of rows.</param>
/// <returns>All solutions.</returns>
public IEnumerable<bool[,]> BacktrackSolve(int n)
{
if (n < 0)
{
throw new ArgumentException(nameof(n));
}

return BacktrackSolve(new bool[n, n], 0);
}

private static IEnumerable<bool[,]> BacktrackSolve(bool[,] board, int col)
{
var solutions = col < board.GetLength(0) - 1
? HandleIntermediateColumn(board, col)
: HandleLastColumn(board);
return solutions;
}

private static IEnumerable<bool[,]> HandleIntermediateColumn(bool[,] board, int col)
{
// To start placing queens on possible spaces within the board.
for (var i = 0; i < board.GetLength(0); i++)
{
if (CanPlace(board, i, col))
{
board[i, col] = true;

foreach (var solution in BacktrackSolve(board, col + 1))
{
yield return solution;
}

board[i, col] = false;
}
}
}

private static IEnumerable<bool[,]> HandleLastColumn(bool[,] board)
{
var n = board.GetLength(0);
for (var i = 0; i < n; i++)
{
if (CanPlace(board, i, n - 1))
{
board[i, n - 1] = true;

yield return (bool[,])board.Clone();

board[i, n - 1] = false;
}
}
}

/// <summary>
/// Checks whether current queen can be placed in current position,
/// outside attacking range of another queen.
/// </summary>
/// <param name="board">Source board.</param>
/// <param name="row">Row coordinate.</param>
/// <param name="col">Col coordinate.</param>
/// <returns>true if queen can be placed in given chessboard coordinates; false otherwise.</returns>
private static bool CanPlace(bool[,] board, int row, int col)
{
// To check whether there are any queens on current row.
for (var i = 0; i < col; i++)
{
if (board[row, i])
{
return false;
}
}

// To check diagonal attack top-left range.
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
{
if (board[i, j])
{
return false;
}
}

// To check diagonal attack bottom-left range.
for (int i = row + 1, j = col - 1; j >= 0 && i < board.GetLength(0); i++, j--)
{
if (board[i, j])
{
return false;
}
}

// Return true if it can use position.
return true;
}
}
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ This repository contains algorithms and data structures implemented in C# for ed
* [Problems](./Algorithms/Problems/)
* [Stable Marriage](./Algorithms/Problems/StableMarriage)
* [Gale-Shapley](./Algorithms/Problems/StableMarriage/GaleShapley.cs)
* [N-Queens](./Algorithms/Problems/NQueens)
* [Backtracking](./Algorithms/Problems/NQueens/BacktrackingNQueensSolver.cs)

* [Data Structures](./DataStructures/)
* [Bit Array](./DataStructures/BitArray.cs)
Expand Down